ci: cypres test setup(#274)
* fix: font size broken * add first cypress test * save onboarding language * load language * get first test to work * update command * install pnpm * update workflow * install * update * test * increase timeout * another update * update 2 * update 3 * upsdada 4 * sfgsfgf 5 * add def supabase url * fix supabase issue * add ci * fix ci * update .env.example * update port * tired * prepare * preview * this is it * ????? * 99999900 * fix log issue * ci * nkkh * ===== * 200000
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
name: Cypress Tests
|
||||
on: [push]
|
||||
jobs:
|
||||
cypress-run:
|
||||
runs-on: ubuntu-latest
|
||||
# Runs tests in parallel with matrix strategy https://docs.cypress.io/guides/guides/parallelization
|
||||
# https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs
|
||||
# Also see warning here https://github.com/cypress-io/github-action#parallel
|
||||
strategy:
|
||||
fail-fast: false # https://github.com/cypress-io/github-action/issues/48
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@v2.0.1
|
||||
with:
|
||||
version: 6
|
||||
# Only install what we need to run the client in-action and tests against it
|
||||
# format: `--filter <packagename>...`
|
||||
run_install: |
|
||||
- args: []
|
||||
- name: Cypress run
|
||||
# Uses the official Cypress GitHub action https://github.com/cypress-io/github-action
|
||||
uses: cypress-io/github-action@v6
|
||||
with:
|
||||
install: false
|
||||
# Starts web server for E2E tests - replace with your own server invocation
|
||||
# https://docs.cypress.io/guides/continuous-integration/introduction#Boot-your-server
|
||||
command: pnpm ci
|
||||
start: pnpm --dir apps/dashboard/ ci
|
||||
wait-on: 'http://localhost:4173' # Waits for above
|
||||
# Records to Cypress Cloud
|
||||
# https://docs.cypress.io/guides/cloud/projects#Set-up-a-project-to-record
|
||||
record: true
|
||||
parallel: true # Runs test in parallel using settings above
|
||||
env:
|
||||
# For recording and parallelization to work you must set your CYPRESS_RECORD_KEY
|
||||
# in GitHub repo → Settings → Secrets → Actions
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
# Creating a token https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -24,3 +24,10 @@ PUBLIC_SERVER_URL=
|
||||
|
||||
# For getting metadata for signup users.
|
||||
PUBLIC_IP_REGISTRY_KEY=
|
||||
|
||||
# Subdomain for admin dashboard
|
||||
PRIVATE_APP_SUBDOMAINS=app
|
||||
|
||||
LEMON_SQUEEZY_API_KEY=
|
||||
LEMON_SQUEEZY_STORE_ID=
|
||||
LEMON_SQUEEZY_WEBHOOK_SECRET=
|
||||
@@ -15,7 +15,8 @@
|
||||
"start": "node build",
|
||||
"prepare": "svelte-kit sync",
|
||||
"test": "jest",
|
||||
"test:watch": "pnpm run test --watch"
|
||||
"test:watch": "pnpm run test --watch",
|
||||
"ci": "cp .env.example .env && vite build && vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.386.0",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export default {
|
||||
supabaseConfig: {
|
||||
bucketPath: 'https://tapaozmyjsuykgerrfkt.supabase.in/storage/v1/object/sign/'
|
||||
}
|
||||
};
|
||||
+1
-1
@@ -33,7 +33,7 @@
|
||||
{:else if hasAtLeastOneTranslation}
|
||||
<Box className="text-center">
|
||||
<AlignBoxTopLeft class="carbon-icon" size={32} />
|
||||
<h3 class="text-md font-normal dark:text-white py-2">
|
||||
<h3 class="text-lg font-normal dark:text-white py-2">
|
||||
{$t('course.navItem.lessons.materials.no_translation')}
|
||||
</h3>
|
||||
</Box>
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
const content = $lessonByTranslation[lessonId][locale];
|
||||
|
||||
if (typeof localeExists[locale] === 'undefined') {
|
||||
console.log('find the language');
|
||||
const { data } = await supabase
|
||||
.from('lesson_language')
|
||||
.select(`id`)
|
||||
@@ -142,9 +141,6 @@
|
||||
materials: LessonPage['materials'],
|
||||
translation: Record<LOCALE, string>
|
||||
) {
|
||||
console.log('isMaterialsEmpty', {
|
||||
translation
|
||||
});
|
||||
const { slide_url, videos, note } = materials;
|
||||
|
||||
return (
|
||||
@@ -249,11 +245,10 @@
|
||||
|
||||
isSaving = true;
|
||||
timeoutId = setTimeout(async () => {
|
||||
console.log('autosaving');
|
||||
const { error } = await saveLesson(updatedMaterials);
|
||||
|
||||
if (error) {
|
||||
console.log('error saving lesson', error);
|
||||
console.error('error saving lesson', error);
|
||||
snackbar.error('snackbar.materials.apology');
|
||||
}
|
||||
isSaving = false;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public';
|
||||
import type { ConfigType } from '$lib/utils/types/config';
|
||||
|
||||
export const config: ConfigType = {
|
||||
supabaseConfig: {
|
||||
url: PUBLIC_SUPABASE_URL || 'http://localhost:54321',
|
||||
anonKey: PUBLIC_SUPABASE_ANON_KEY || 'anon-key'
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PUBLIC_SUPABASE_URL } from '$env/static/public';
|
||||
import { PRIVATE_SUPABASE_SERVICE_ROLE } from '$env/static/private';
|
||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||
import { config } from '$lib/config';
|
||||
|
||||
export let supabase: SupabaseClient;
|
||||
|
||||
@@ -12,7 +12,7 @@ export let supabase: SupabaseClient;
|
||||
export const getServerSupabase = () => {
|
||||
if (supabase) return supabase;
|
||||
|
||||
supabase = createClient(PUBLIC_SUPABASE_URL, PRIVATE_SUPABASE_SERVICE_ROLE, {
|
||||
supabase = createClient(config.supabaseConfig.url, PRIVATE_SUPABASE_SERVICE_ROLE || '', {
|
||||
auth: {
|
||||
persistSession: false,
|
||||
autoRefreshToken: false,
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public';
|
||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||
import type { ConfigType } from '../types';
|
||||
import { config } from '$lib/config';
|
||||
|
||||
export let supabase: SupabaseClient;
|
||||
|
||||
export const getSupabase = (config?: ConfigType) => {
|
||||
export const getSupabase = () => {
|
||||
if (supabase) return supabase;
|
||||
config = {
|
||||
supabaseConfig: {
|
||||
url: PUBLIC_SUPABASE_URL || '',
|
||||
anonKey: PUBLIC_SUPABASE_ANON_KEY || ''
|
||||
}
|
||||
};
|
||||
|
||||
supabase = createClient(config.supabaseConfig.url, config.supabaseConfig.anonKey);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getCurrentOrg } from '$lib/utils/services/org';
|
||||
import { getSupabase, supabase } from '$lib/utils/functions/supabase';
|
||||
import { loadTranslations } from '$lib/utils/functions/translations';
|
||||
import type { CurrentOrg } from '$lib/utils/types/org';
|
||||
import { PRIVATE_APP_SUBDOMAINS } from '$env/static/private';
|
||||
|
||||
if (!supabase) {
|
||||
getSupabase();
|
||||
@@ -32,6 +33,8 @@ export const load = async ({ url, cookies }): Promise<LoadOutput> => {
|
||||
const matches = url.host.match(/([a-z 0-9 -]+).*classroomio[.]com/);
|
||||
const subdomain = matches?.[1] ?? '';
|
||||
|
||||
const isDev = dev || url.host.includes('localhost');
|
||||
|
||||
if (!url.host.includes('.classroomio.com') && !url.host.includes('localhost')) {
|
||||
// TODO: We can verify if custom domain here
|
||||
return response;
|
||||
@@ -44,13 +47,12 @@ export const load = async ({ url, cookies }): Promise<LoadOutput> => {
|
||||
response.orgSiteName = debugMode ? _orgSiteName : subdomain;
|
||||
response.org = (await getCurrentOrg(response.orgSiteName, true)) || null;
|
||||
|
||||
|
||||
if (!response.org && !dev) {
|
||||
if (!response.org && !isDev) {
|
||||
throw redirect(307, 'https://app.classroomio.com/404?type=org');
|
||||
}
|
||||
} else if (subdomain === 'play' || debugPlay === 'true') {
|
||||
response.skipAuth = true;
|
||||
} else if (subdomain !== 'app' && !dev) {
|
||||
} else if (PRIVATE_APP_SUBDOMAINS.split(',').includes(subdomain) && !isDev) {
|
||||
throw redirect(307, 'https://app.classroomio.com');
|
||||
}
|
||||
|
||||
@@ -67,7 +69,7 @@ function getInitialLocale(): string {
|
||||
if (browser) {
|
||||
try {
|
||||
return window.navigator.language.split('-')[0];
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
return 'en';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,11 +218,8 @@
|
||||
$globalStore.isDark = localStorage.getItem('mode') === 'dark';
|
||||
toggleBodyByMode($globalStore.isDark);
|
||||
|
||||
const theme = localStorage.getItem('theme');
|
||||
if (data.isOrgSite && data.org?.theme) {
|
||||
setTheme(data.org?.theme);
|
||||
} else if (theme) {
|
||||
setTheme(theme);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
triggerSendEmail,
|
||||
NOTIFICATION_NAME
|
||||
} from '$lib/utils/services/notification/notification';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { handleLocaleChange, t } from '$lib/utils/functions/translations';
|
||||
import { LOCALE } from '$lib/utils/types';
|
||||
|
||||
interface OnboardingField {
|
||||
fullname?: string;
|
||||
@@ -201,6 +202,13 @@
|
||||
if (fields.fullname) {
|
||||
$profile.fullname = fields.fullname;
|
||||
}
|
||||
if (fields.locale) {
|
||||
$profile.locale = fields.locale;
|
||||
|
||||
if (fields.locale !== LOCALE.EN) {
|
||||
handleLocaleChange(fields.locale);
|
||||
}
|
||||
}
|
||||
triggerSendEmail(NOTIFICATION_NAME.VERIFY_EMAIL, {
|
||||
to: $profile.email,
|
||||
profileId: $profile.id,
|
||||
@@ -286,7 +294,6 @@
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
|
|
||||
{:else}
|
||||
<!-- Goal/Source Question -->
|
||||
<div id="goal-question" class="flex items-center flex-col mb-6">
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
// ...rest of the Cypress project config
|
||||
projectId: '56i2dj',
|
||||
defaultCommandTimeout: 200000,
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
describe('classroomio dashboard', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('http://localhost:4173');
|
||||
});
|
||||
|
||||
it('shows the signup and login button', () => {
|
||||
cy.get('nav button').should('have.length', 2);
|
||||
|
||||
cy.get('nav button').first().contains('Login');
|
||||
cy.get('nav button').last().contains('Sign Up');
|
||||
});
|
||||
|
||||
it('can redirect to login page', () => {
|
||||
cy.get('nav button').contains('Login').click();
|
||||
|
||||
cy.url().should('contain', '/login');
|
||||
|
||||
cy.contains('Welcome back').should('be.visible');
|
||||
|
||||
cy.get('form button[type="submit"]').contains('Log In').should('be.visible');
|
||||
});
|
||||
|
||||
it('can redirect to signup page', () => {
|
||||
cy.get('nav button').contains('Sign Up').click();
|
||||
|
||||
cy.url().should('contain', '/signup');
|
||||
|
||||
cy.contains('Create a free account').should('be.visible');
|
||||
|
||||
cy.get('form button[type="submit"]').contains('Create Account').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
+4
-1
@@ -8,11 +8,14 @@
|
||||
"lint": "turbo run lint",
|
||||
"clean": "turbo clean && rm -rf node_modules",
|
||||
"format": "prettier . --write",
|
||||
"release": "standard-version"
|
||||
"release": "standard-version",
|
||||
"ci": "cypress run"
|
||||
},
|
||||
"license": "MIT",
|
||||
"packageManager": "pnpm@8.6.13",
|
||||
"devDependencies": {
|
||||
"concurrently": "^8.2.2",
|
||||
"cypress": "^13.8.0",
|
||||
"eslint": "^8.54.0",
|
||||
"prettier": "^3.1.0",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
|
||||
Generated
+650
-31
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user