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.
|
# For getting metadata for signup users.
|
||||||
PUBLIC_IP_REGISTRY_KEY=
|
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",
|
"start": "node build",
|
||||||
"prepare": "svelte-kit sync",
|
"prepare": "svelte-kit sync",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "pnpm run test --watch"
|
"test:watch": "pnpm run test --watch",
|
||||||
|
"ci": "cp .env.example .env && vite build && vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.386.0",
|
"@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}
|
{:else if hasAtLeastOneTranslation}
|
||||||
<Box className="text-center">
|
<Box className="text-center">
|
||||||
<AlignBoxTopLeft class="carbon-icon" size={32} />
|
<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')}
|
{$t('course.navItem.lessons.materials.no_translation')}
|
||||||
</h3>
|
</h3>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -83,7 +83,6 @@
|
|||||||
const content = $lessonByTranslation[lessonId][locale];
|
const content = $lessonByTranslation[lessonId][locale];
|
||||||
|
|
||||||
if (typeof localeExists[locale] === 'undefined') {
|
if (typeof localeExists[locale] === 'undefined') {
|
||||||
console.log('find the language');
|
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('lesson_language')
|
.from('lesson_language')
|
||||||
.select(`id`)
|
.select(`id`)
|
||||||
@@ -142,9 +141,6 @@
|
|||||||
materials: LessonPage['materials'],
|
materials: LessonPage['materials'],
|
||||||
translation: Record<LOCALE, string>
|
translation: Record<LOCALE, string>
|
||||||
) {
|
) {
|
||||||
console.log('isMaterialsEmpty', {
|
|
||||||
translation
|
|
||||||
});
|
|
||||||
const { slide_url, videos, note } = materials;
|
const { slide_url, videos, note } = materials;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -249,11 +245,10 @@
|
|||||||
|
|
||||||
isSaving = true;
|
isSaving = true;
|
||||||
timeoutId = setTimeout(async () => {
|
timeoutId = setTimeout(async () => {
|
||||||
console.log('autosaving');
|
|
||||||
const { error } = await saveLesson(updatedMaterials);
|
const { error } = await saveLesson(updatedMaterials);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log('error saving lesson', error);
|
console.error('error saving lesson', error);
|
||||||
snackbar.error('snackbar.materials.apology');
|
snackbar.error('snackbar.materials.apology');
|
||||||
}
|
}
|
||||||
isSaving = false;
|
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 { PRIVATE_SUPABASE_SERVICE_ROLE } from '$env/static/private';
|
||||||
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||||
|
import { config } from '$lib/config';
|
||||||
|
|
||||||
export let supabase: SupabaseClient;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export let supabase: SupabaseClient;
|
|||||||
export const getServerSupabase = () => {
|
export const getServerSupabase = () => {
|
||||||
if (supabase) return supabase;
|
if (supabase) return supabase;
|
||||||
|
|
||||||
supabase = createClient(PUBLIC_SUPABASE_URL, PRIVATE_SUPABASE_SERVICE_ROLE, {
|
supabase = createClient(config.supabaseConfig.url, PRIVATE_SUPABASE_SERVICE_ROLE || '', {
|
||||||
auth: {
|
auth: {
|
||||||
persistSession: false,
|
persistSession: false,
|
||||||
autoRefreshToken: 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 { createClient, SupabaseClient } from '@supabase/supabase-js';
|
||||||
import type { ConfigType } from '../types';
|
import { config } from '$lib/config';
|
||||||
|
|
||||||
export let supabase: SupabaseClient;
|
export let supabase: SupabaseClient;
|
||||||
|
|
||||||
export const getSupabase = (config?: ConfigType) => {
|
export const getSupabase = () => {
|
||||||
if (supabase) return supabase;
|
if (supabase) return supabase;
|
||||||
config = {
|
|
||||||
supabaseConfig: {
|
|
||||||
url: PUBLIC_SUPABASE_URL || '',
|
|
||||||
anonKey: PUBLIC_SUPABASE_ANON_KEY || ''
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
supabase = createClient(config.supabaseConfig.url, config.supabaseConfig.anonKey);
|
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 { getSupabase, supabase } from '$lib/utils/functions/supabase';
|
||||||
import { loadTranslations } from '$lib/utils/functions/translations';
|
import { loadTranslations } from '$lib/utils/functions/translations';
|
||||||
import type { CurrentOrg } from '$lib/utils/types/org';
|
import type { CurrentOrg } from '$lib/utils/types/org';
|
||||||
|
import { PRIVATE_APP_SUBDOMAINS } from '$env/static/private';
|
||||||
|
|
||||||
if (!supabase) {
|
if (!supabase) {
|
||||||
getSupabase();
|
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 matches = url.host.match(/([a-z 0-9 -]+).*classroomio[.]com/);
|
||||||
const subdomain = matches?.[1] ?? '';
|
const subdomain = matches?.[1] ?? '';
|
||||||
|
|
||||||
|
const isDev = dev || url.host.includes('localhost');
|
||||||
|
|
||||||
if (!url.host.includes('.classroomio.com') && !url.host.includes('localhost')) {
|
if (!url.host.includes('.classroomio.com') && !url.host.includes('localhost')) {
|
||||||
// TODO: We can verify if custom domain here
|
// TODO: We can verify if custom domain here
|
||||||
return response;
|
return response;
|
||||||
@@ -43,14 +46,13 @@ export const load = async ({ url, cookies }): Promise<LoadOutput> => {
|
|||||||
response.isOrgSite = debugMode || answer;
|
response.isOrgSite = debugMode || answer;
|
||||||
response.orgSiteName = debugMode ? _orgSiteName : subdomain;
|
response.orgSiteName = debugMode ? _orgSiteName : subdomain;
|
||||||
response.org = (await getCurrentOrg(response.orgSiteName, true)) || null;
|
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');
|
throw redirect(307, 'https://app.classroomio.com/404?type=org');
|
||||||
}
|
}
|
||||||
} else if (subdomain === 'play' || debugPlay === 'true') {
|
} else if (subdomain === 'play' || debugPlay === 'true') {
|
||||||
response.skipAuth = 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');
|
throw redirect(307, 'https://app.classroomio.com');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ function getInitialLocale(): string {
|
|||||||
if (browser) {
|
if (browser) {
|
||||||
try {
|
try {
|
||||||
return window.navigator.language.split('-')[0];
|
return window.navigator.language.split('-')[0];
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
return 'en';
|
return 'en';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,11 +218,8 @@
|
|||||||
$globalStore.isDark = localStorage.getItem('mode') === 'dark';
|
$globalStore.isDark = localStorage.getItem('mode') === 'dark';
|
||||||
toggleBodyByMode($globalStore.isDark);
|
toggleBodyByMode($globalStore.isDark);
|
||||||
|
|
||||||
const theme = localStorage.getItem('theme');
|
|
||||||
if (data.isOrgSite && data.org?.theme) {
|
if (data.isOrgSite && data.org?.theme) {
|
||||||
setTheme(data.org?.theme);
|
setTheme(data.org?.theme);
|
||||||
} else if (theme) {
|
|
||||||
setTheme(theme);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
triggerSendEmail,
|
triggerSendEmail,
|
||||||
NOTIFICATION_NAME
|
NOTIFICATION_NAME
|
||||||
} from '$lib/utils/services/notification/notification';
|
} 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 {
|
interface OnboardingField {
|
||||||
fullname?: string;
|
fullname?: string;
|
||||||
@@ -201,6 +202,13 @@
|
|||||||
if (fields.fullname) {
|
if (fields.fullname) {
|
||||||
$profile.fullname = 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, {
|
triggerSendEmail(NOTIFICATION_NAME.VERIFY_EMAIL, {
|
||||||
to: $profile.email,
|
to: $profile.email,
|
||||||
profileId: $profile.id,
|
profileId: $profile.id,
|
||||||
@@ -286,7 +294,6 @@
|
|||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Goal/Source Question -->
|
<!-- Goal/Source Question -->
|
||||||
<div id="goal-question" class="flex items-center flex-col mb-6">
|
<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",
|
"lint": "turbo run lint",
|
||||||
"clean": "turbo clean && rm -rf node_modules",
|
"clean": "turbo clean && rm -rf node_modules",
|
||||||
"format": "prettier . --write",
|
"format": "prettier . --write",
|
||||||
"release": "standard-version"
|
"release": "standard-version",
|
||||||
|
"ci": "cypress run"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"packageManager": "pnpm@8.6.13",
|
"packageManager": "pnpm@8.6.13",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"concurrently": "^8.2.2",
|
||||||
|
"cypress": "^13.8.0",
|
||||||
"eslint": "^8.54.0",
|
"eslint": "^8.54.0",
|
||||||
"prettier": "^3.1.0",
|
"prettier": "^3.1.0",
|
||||||
"prettier-plugin-svelte": "^3.1.2",
|
"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