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:
Rotimi Best
2024-04-20 04:39:16 +01:00
committed by GitHub
parent 4f9844bdd2
commit a95be6d620
19 changed files with 827 additions and 65 deletions
+42
View File
@@ -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/[email protected]
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 }}
+7
View File
@@ -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=
+2 -1
View File
@@ -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",
-5
View File
@@ -1,5 +0,0 @@
export default {
supabaseConfig: {
bucketPath: 'https://tapaozmyjsuykgerrfkt.supabase.in/storage/v1/object/sign/'
}
};
@@ -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;
+9
View File
@@ -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);
+6 -4
View File
@@ -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';
} }
} }
-3
View File
@@ -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">
+10
View File
@@ -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');
});
});
+5
View File
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "[email protected]",
"body": "Fixtures are a great way to mock data for responses to routes"
}
+25
View File
@@ -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) => { ... })
+20
View File
@@ -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
View File
@@ -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": "[email protected]", "packageManager": "[email protected]",
"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",
+650 -31
View File
File diff suppressed because it is too large Load Diff