generated from graasp/graasp-repo
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
381 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
name: Cypress CI | ||
|
||
on: | ||
push: | ||
branches: | ||
- 'main' | ||
merge_group: | ||
pull_request: | ||
|
||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | ||
cancel-in-progress: false | ||
|
||
jobs: | ||
cypress: | ||
name: Cypress | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 50 | ||
steps: | ||
- name: Check out code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Setup Node | ||
uses: actions/setup-node@v4 | ||
|
||
- name: Yarn Install and Cache | ||
uses: graasp/graasp-deploy/.github/actions/yarn-install-and-cache@v1 | ||
with: | ||
cypress: true | ||
|
||
# type check | ||
- name: Type-check code | ||
run: tsc --noEmit | ||
|
||
- name: Build App | ||
run: NODE_OPTIONS=--max-old-space-size=8192 yarn build:test | ||
shell: bash | ||
env: | ||
VITE_PORT: ${{ vars.VITE_PORT }} | ||
VITE_VERSION: ${{ vars.VITE_VERSION }} | ||
VITE_GRAASP_DOMAIN: ${{ vars.VITE_GRAASP_DOMAIN }} | ||
VITE_GRAASP_API_HOST: ${{ vars.VITE_GRAASP_API_HOST }} | ||
VITE_GRAASP_AUTH_HOST: ${{ vars.VITE_GRAASP_AUTH_HOST }} | ||
VITE_GRAASP_PLAYER_HOST: ${{ vars.VITE_GRAASP_PLAYER_HOST }} | ||
VITE_GRAASP_LIBRARY_HOST: ${{ vars.VITE_GRAASP_LIBRARY_HOST }} | ||
VITE_GRAASP_ANALYZER_HOST: ${{ vars.VITE_GRAASP_ANALYZER_HOST }} | ||
VITE_SHOW_NOTIFICATIONS: ${{ vars.VITE_SHOW_NOTIFICATIONS }} | ||
VITE_GRAASP_REDIRECTION_HOST: ${{ vars.VITE_GRAASP_REDIRECTION_HOST }} | ||
|
||
# use the Cypress GitHub Action to run Cypress tests within the chrome browser | ||
- name: Cypress run | ||
uses: cypress-io/github-action@v6 | ||
with: | ||
install: false | ||
# we launch the app in preview mode to avoid issues with hmr websockets from vite polluting the mocks | ||
start: yarn preview:test | ||
browser: chrome | ||
quiet: true | ||
config-file: cypress.config.ts | ||
cache-key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} | ||
env: | ||
VITE_PORT: ${{ vars.VITE_PORT }} | ||
VITE_VERSION: ${{ vars.VITE_VERSION }} | ||
VITE_GRAASP_DOMAIN: ${{ vars.VITE_GRAASP_DOMAIN }} | ||
VITE_GRAASP_API_HOST: ${{ vars.VITE_GRAASP_API_HOST }} | ||
VITE_GRAASP_AUTH_HOST: ${{ vars.VITE_GRAASP_AUTH_HOST }} | ||
VITE_GRAASP_PLAYER_HOST: ${{ vars.VITE_GRAASP_PLAYER_HOST }} | ||
VITE_GRAASP_LIBRARY_HOST: ${{ vars.VITE_GRAASP_LIBRARY_HOST }} | ||
VITE_GRAASP_ANALYZER_HOST: ${{ vars.VITE_GRAASP_ANALYZER_HOST }} | ||
VITE_SHOW_NOTIFICATIONS: ${{ vars.VITE_SHOW_NOTIFICATIONS }} | ||
VITE_GRAASP_REDIRECTION_HOST: ${{ vars.VITE_GRAASP_REDIRECTION_HOST }} | ||
|
||
# after the test run completes | ||
# store any screenshots | ||
# NOTE: screenshots will be generated only if E2E test failed | ||
# thus we store screenshots only on failures | ||
- uses: actions/upload-artifact@v4 | ||
if: failure() | ||
with: | ||
name: cypress-screenshots | ||
path: cypress/screenshots | ||
|
||
- name: coverage report | ||
run: npx nyc report --reporter=text-summary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { defineConfig } from 'cypress'; | ||
|
||
export default defineConfig({ | ||
e2e: { | ||
env: { | ||
VITE_GRAASP_REDIRECTION_HOST: process.env.VITE_GRAASP_REDIRECTION_HOST, | ||
VITE_GRAASP_DOMAIN: process.env.VITE_GRAASP_DOMAIN, | ||
VITE_GRAASP_API_HOST: process.env.VITE_GRAASP_API_HOST, | ||
VITE_SHOW_NOTIFICATIONS: false, | ||
VITE_GRAASP_AUTH_HOST: process.env.VITE_GRAASP_AUTH_HOST, | ||
VITE_GRAASP_PLAYER_HOST: process.env.VITE_GRAASP_PLAYER_HOST, | ||
VITE_GRAASP_ANALYZER_HOST: process.env.VITE_GRAASP_ANALYZER_HOST, | ||
VITE_GRAASP_LIBRARY_HOST: process.env.VITE_GRAASP_LIBRARY_HOST, | ||
VITE_GRAASP_ACCOUNT_HOST: process.env.VITE_GRAASP_ACCOUNT_HOST, | ||
}, | ||
setupNodeEvents(_on, _config) { | ||
// implement node event listeners here | ||
}, | ||
baseUrl: 'http://localhost:3114', | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
declare namespace Cypress { | ||
interface Chainable { | ||
// todo: improve types | ||
setUpApi(args: any): Chainable; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
describe('template spec', () => { | ||
it('passes', () => { | ||
cy.setUpApi(); | ||
cy.visit('/'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { MemberFactory } from '@graasp/sdk'; | ||
|
||
// eslint-disable-next-line import/prefer-default-export | ||
export const CURRENT_MEMBER = MemberFactory(); | ||
|
||
export const MEMBERS = []; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/// <reference types="cypress" /> | ||
import { CookieKeys } from '@graasp/sdk'; | ||
|
||
import { CURRENT_MEMBER, MEMBERS } from '../fixtures/members'; | ||
import { | ||
mockEditMember, | ||
mockGetAvatarUrl, | ||
mockGetCurrentMember, | ||
mockGetMember, | ||
mockPostAvatar, | ||
mockSignInRedirection, | ||
mockSignOut, | ||
mockUpdatePassword, | ||
} from './server'; | ||
|
||
Cypress.Commands.add( | ||
'setUpApi', | ||
({ | ||
members = Object.values(MEMBERS), | ||
currentMember = CURRENT_MEMBER, | ||
getCurrentMemberError = false, | ||
editMemberError = false, | ||
getAvatarUrlError = false, | ||
postAvatarError = false, | ||
updatePasswordError = false, | ||
} = {}) => { | ||
const cachedMembers = JSON.parse(JSON.stringify(members)); | ||
|
||
// hide cookie banner by default | ||
cy.setCookie(CookieKeys.AcceptCookies, 'true'); | ||
|
||
mockGetMember(cachedMembers); | ||
|
||
mockGetCurrentMember(currentMember, getCurrentMemberError); | ||
|
||
mockSignInRedirection(); | ||
|
||
mockSignOut(); | ||
|
||
mockEditMember(members, editMemberError); | ||
|
||
mockGetAvatarUrl(members, getAvatarUrlError); | ||
|
||
mockPostAvatar(postAvatarError); | ||
|
||
mockUpdatePassword(members, updatePasswordError); | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// *********************************************************** | ||
// This example support/e2e.ts 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') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import { API_ROUTES } from '@graasp/query-client'; | ||
import { HttpMethod, Member, buildSignInPath } from '@graasp/sdk'; | ||
|
||
import { StatusCodes } from 'http-status-codes'; | ||
|
||
import { CURRENT_MEMBER } from '../fixtures/members'; | ||
import { ID_FORMAT, MemberForTest } from './utils'; | ||
|
||
const { | ||
buildGetMember, | ||
GET_CURRENT_MEMBER_ROUTE, | ||
SIGN_OUT_ROUTE, | ||
buildPatchMember, | ||
buildUploadAvatarRoute, | ||
buildUpdateMemberPasswordRoute, | ||
} = API_ROUTES; | ||
|
||
export const SIGN_IN_PATH = buildSignInPath({ | ||
host: Cypress.env('VITE_GRAASP_AUTH_HOST'), | ||
}); | ||
const API_HOST = Cypress.env('VITE_GRAASP_API_HOST'); | ||
export const AVATAR_LINK = 'https://picsum.photos/200/200'; | ||
|
||
export const redirectionReply = { | ||
headers: { 'content-type': 'application/json' }, | ||
statusCode: StatusCodes.OK, | ||
}; | ||
|
||
export const mockGetCurrentMember = ( | ||
currentMember = CURRENT_MEMBER, | ||
shouldThrowError = false, | ||
): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Get, | ||
url: `${API_HOST}/${GET_CURRENT_MEMBER_ROUTE}`, | ||
}, | ||
({ reply }) => { | ||
if (shouldThrowError) { | ||
return reply({ | ||
statusCode: StatusCodes.INTERNAL_SERVER_ERROR, | ||
body: null, | ||
}); | ||
} | ||
|
||
// might reply empty user when signed out | ||
return reply({ statusCode: StatusCodes.OK, body: currentMember }); | ||
}, | ||
).as('getCurrentMember'); | ||
}; | ||
|
||
export const mockGetMember = (members: Member[]): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Get, | ||
url: new RegExp(`${API_HOST}/${buildGetMember(ID_FORMAT)}$`), | ||
}, | ||
({ url, reply }) => { | ||
const memberId = url.slice(API_HOST.length).split('/')[2]; | ||
const member = members.find((m) => m.id === memberId); | ||
|
||
// member does not exist in db | ||
if (!member) { | ||
return reply({ | ||
statusCode: StatusCodes.NOT_FOUND, | ||
}); | ||
} | ||
|
||
return reply({ | ||
body: member, | ||
statusCode: StatusCodes.OK, | ||
}); | ||
}, | ||
).as('getMember'); | ||
}; | ||
|
||
export const mockEditMember = ( | ||
_members: Member[], | ||
shouldThrowError: boolean, | ||
): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Patch, | ||
url: new RegExp(`${API_HOST}/${buildPatchMember(ID_FORMAT)}`), | ||
}, | ||
({ reply }) => { | ||
if (shouldThrowError) { | ||
return reply({ statusCode: StatusCodes.BAD_REQUEST }); | ||
} | ||
|
||
return reply('edit member'); | ||
}, | ||
).as('editMember'); | ||
}; | ||
|
||
export const mockSignInRedirection = (): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Get, | ||
url: SIGN_IN_PATH, | ||
}, | ||
({ reply }) => { | ||
reply(redirectionReply); | ||
}, | ||
).as('signInRedirection'); | ||
}; | ||
|
||
export const mockSignOut = (): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Get, | ||
url: new RegExp(SIGN_OUT_ROUTE), | ||
}, | ||
({ reply }) => { | ||
reply(redirectionReply); | ||
}, | ||
).as('signOut'); | ||
}; | ||
|
||
export const mockGetAvatarUrl = ( | ||
members: MemberForTest[], | ||
shouldThrowError: boolean, | ||
): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Get, | ||
// TODO: include all sizes | ||
url: new RegExp( | ||
`${API_HOST}/members/${ID_FORMAT}/avatar/small\\?replyUrl\\=true`, | ||
), | ||
}, | ||
({ reply, url }) => { | ||
if (shouldThrowError) { | ||
return reply({ statusCode: StatusCodes.BAD_REQUEST }); | ||
} | ||
|
||
const [link] = url.split('?'); | ||
const id = link.slice(API_HOST.length).split('/')[2]; | ||
|
||
const { thumbnails } = | ||
members.find(({ id: thisId }) => id === thisId) ?? {}; | ||
if (!thumbnails) { | ||
return reply({ statusCode: StatusCodes.NOT_FOUND }); | ||
} | ||
// TODO: REPLY URL | ||
return reply(AVATAR_LINK); | ||
}, | ||
).as('downloadAvatarUrl'); | ||
}; | ||
|
||
export const mockPostAvatar = (shouldThrowError: boolean): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Post, | ||
url: new RegExp(`${buildUploadAvatarRoute()}`), | ||
}, | ||
({ reply }) => { | ||
if (shouldThrowError) { | ||
return reply({ statusCode: StatusCodes.BAD_REQUEST }); | ||
} | ||
|
||
return reply({ statusCode: StatusCodes.OK }); | ||
}, | ||
).as('uploadAvatar'); | ||
}; | ||
|
||
export const mockUpdatePassword = ( | ||
_members: Member[], | ||
shouldThrowError: boolean, | ||
): void => { | ||
cy.intercept( | ||
{ | ||
method: HttpMethod.Patch, | ||
url: new RegExp(`${API_HOST}/${buildUpdateMemberPasswordRoute()}`), | ||
}, | ||
({ reply }) => { | ||
if (shouldThrowError) { | ||
return reply({ statusCode: StatusCodes.BAD_REQUEST }); | ||
} | ||
|
||
return reply('update password'); | ||
}, | ||
).as('updatePassword'); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { CompleteMember } from '@graasp/sdk'; | ||
|
||
export const ID_FORMAT = '(?=.*[0-9])(?=.*[a-zA-Z])([a-z0-9-]+)'; | ||
|
||
// TODO: not ideal, to change? | ||
export type MemberForTest = CompleteMember & { thumbnails?: string }; |