diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index 01859332..252fd782 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -36,6 +36,12 @@ jobs: - name: Run Cypress tests uses: cypress-io/github-action@v6 + env: + AUTH0_DOMAIN: ${{ secrets.AUTH0_DOMAIN }} + AUTH0_USERNAME: ${{ secrets.AUTH0_USERNAME }} + AUTH0_PASSWORD: ${{ secrets.AUTH0_PASSWORD }} + with: + command: yarn test:e2e:ci - name: Upload screenshots uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 90667264..781f0268 100644 --- a/.gitignore +++ b/.gitignore @@ -22,5 +22,9 @@ eslint-results.sarif cypress/videos *.DS_Store +# Env .env +.env.test + +# VS Code .vscode diff --git a/cypress.config.ts b/cypress.config.ts index bfeced1d..4641e285 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,15 +1,30 @@ +/* eslint-disable @typescript-eslint/no-namespace */ import { defineConfig } from 'cypress' +declare global { + namespace NodeJS { + interface ProcessEnv { + AUTH0_DOMAIN: string + AUTH0_USERNAME: string + AUTH0_PASSWORD: string + } + } +} + const config = defineConfig({ projectId: 'brkojt', e2e: { + env: { + AUTH0_DOMAIN: process.env.AUTH0_DOMAIN, + AUTH0_USERNAME: process.env.AUTH0_USERNAME, + AUTH0_PASSWORD: process.env.AUTH0_PASSWORD + }, setupNodeEvents() { }, // Path to e2e specs folder specPattern: './test/cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', // Path to the fake data fixturesFolder: './test/fake_data', - supportFolder: './test/cypress/support', - supportFile: './test/cypress/support/e2e.js', + supportFile: './test/cypress/support/e2e.ts', baseUrl: 'http://localhost:3000', testIsolation: false, experimentalRunAllSpecs: true, diff --git a/eslint.config.js b/eslint.config.js index 50ee4c2b..fc63ca95 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,7 +2,7 @@ import globals from 'globals' import eslintJsPlugin from '@eslint/js' import tseslint from 'typescript-eslint' import stylistic from '@stylistic/eslint-plugin' -import pluginCypress from 'eslint-plugin-cypress' +import pluginCypress from 'eslint-plugin-cypress/flat' import pluginVue from 'eslint-plugin-vue' import withNuxt from './.nuxt/eslint.config.mjs' @@ -14,8 +14,8 @@ export default withNuxt( '.output/*', '.nuxt/*', 'coverage/*', - 'cypress/*', '.yarn/*', + 'test/vitest', 'typedefs/gqlTypes.ts' ] }, @@ -36,7 +36,7 @@ export default withNuxt( '@typescript-eslint': tseslint.plugin, '@stylistic': stylistic }, - ignores: ['./typeDefs/gqlTypes.ts', './typesgeneratorconfig.ts', 'cypress/**/*'], + ignores: ['./typeDefs/gqlTypes.ts', './typesgeneratorconfig.ts'], rules: { ...tseslint.configs.recommended, // TS specific rules @@ -94,32 +94,12 @@ export default withNuxt( 'vue/html-indent': ['error', 4] } }, - // Linting for tests (cypress + pinia) + // Linting for Cypress (https://www.npmjs.com/package/eslint-plugin-cypress) { - languageOptions: { - globals: { - cy: true, - it: true, - describe: true, - context: true, - beforeEach: true, - before: true - } - }, - files: ['cypress/e2e/*.ts', 'test/**/*'], - plugins: { - cypress: pluginCypress - }, + files: ['test/cypress/**/*'], + ...pluginCypress.configs.recommended, rules: { - //this is to support chai chaining syntax - '@typescript-eslint/no-unused-expressions': 'off' - // 'cypress/no-assigning-return-values": "error", - // "cypress/no-unnecessary-waiting": "error", - // "cypress/assertion-before-screenshot": "warn", - // "cypress/no-force": "warn", - // "cypress/no-async-tests": "error", - // "cypress/no-async-before": "error", - // "cypress/no-pause": "error" + 'cypress/no-unnecessary-waiting': 'off' } } ) diff --git a/package.json b/package.json index 6585b3db..6b02fe6c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "test": "yarn test:pinia --run && yarn test:e2e", "test:pinia": "nuxi prepare && nuxi generate && vitest", "test:pinia:coverage": "vitest run --coverage", - "test:e2e": "cypress run --e2e --browser chrome --record --key 04a1fe54-0011-407b-b7d7-fcb9b2daf1e6" + "test:e2e": "dotenv -e .env.test -o -- cypress open", + "test:e2e:ci": "dotenv -e .env.test -o -- cypress run --e2e" }, "packageManager": "yarn@4.4.0", "engines": { @@ -59,6 +60,7 @@ "autoprefixer": "^10.4.20", "cypress": "^13.13.2", "cypress-plugin-tab": "^1.0.5", + "dotenv-cli": "^8.0.0", "eslint": "^9.8.0", "eslint-plugin-cypress": "^3.4.0", "eslint-plugin-json": "^4.0.1", diff --git a/test/cypress/e2e/moderationDashboard.cy.ts b/test/cypress/e2e/moderationDashboard.cy.ts index bf67f113..35ba5d59 100644 --- a/test/cypress/e2e/moderationDashboard.cy.ts +++ b/test/cypress/e2e/moderationDashboard.cy.ts @@ -1,417 +1,109 @@ -import 'cypress-real-events' -import 'cypress-plugin-tab' import enUS from '../../../i18n/locales/en.json' +import fakeSubmissionResponse from '../../fake_data/moderation_dashboard/fakeSubmissionResponse.json' +import { aliasQuery } from '../utils' -const FAKE_SUBMISSION_RESPONSE_PATH = 'moderation_dashboard/fakeSubmissionResponse.json' - -describe( - 'Moderation dashboard', - () => { - // Before starting the execution, we need to load the fixture data using "this" context of the - // test object. We need to use regular function() callbacks instead of arrow functions when - // we need to access the "this" context. - before(function() { - cy.fixture(FAKE_SUBMISSION_RESPONSE_PATH).then(fakeSubmissionResponse => { - this.fakeSubmissionResponse = fakeSubmissionResponse - }) - }) - - context('Landscape mode', () => { - beforeEach(function() { - // The resolution is in the beforeEach() instead of before() to - // prevent Cypress from defaulting to other screen sizes between tests. - cy.viewport('macbook-16') - cy.visit('/login') - Cypress.session.clearCurrentSessionData() - - // This intercepts the call to the GraphQL API in order to use fake data in the tests to protect the real data. - cy.intercept('POST', '**/', req => { - if (req.body.query && req.body.query.includes('query Submissions')) { - req.reply({ - statusCode: 200, - body: this.fakeSubmissionResponse - }) - } else { - req.continue() - } - }).as('getSubmissions') - - cy.origin('https://findadoc.jp.auth0.com/', () => { - cy.get('input#username').should('be.visible').type('findadoctest@proton.me') - cy.get('[data-action-button-primary]').should('be.visible').click() - cy.get('input#password').should('be.visible').type('vCnL5J8agHg6m2f') - cy.get('[data-action-button-primary]').should('be.visible').click() - }) - - cy.url({ timeout: 10000 }).should('equal', 'http://localhost:3000/') - - /* Chaining of visit was used here to make sure the user was logged in and that it would - 100 percent visit moderation */ - cy.get('[data-testid=top-nav-mod-link]').click().visit('/moderation') - - cy.url({ timeout: 10000 }).should('include', '/moderation') - - cy.wait('@getSubmissions') - }) - - it('shows mod dashboard left navbar buttons with correct counts and functionality', () => { - // The number for include text is for the status in the fake data. - cy.get('[data-testid=mod-dashboard-leftnav-for-review]') - .should('exist') - .should( - 'include.text', - enUS.modDashboardLeftNav.forReview - ) - .should( - 'include.text', - '1' - ) - - cy.get('[data-testid=mod-dashboard-leftnav-approved]') - .should('exist') - .should( - 'include.text', - enUS.modDashboardLeftNav.approved - ) - .should( - 'include.text', - '1' - ) - - cy.get('[data-testid=mod-dashboard-leftnav-rejected]') - .should('exist') - .should( - 'include.text', - enUS.modDashboardLeftNav.rejected - ) - .should( - 'include.text', - '0' - ) - - cy.get('[data-testid="mod-submission-list-item-1"]').should('exist') - - cy.get('[data-testid=mod-dashboard-leftnav-approved]') - .click() - - cy.get('[data-testid="mod-submission-list-item-1"]').should('exist') - - cy.get('[data-testid=mod-dashboard-leftnav-rejected]') - .click() - - cy.get('[data-testid="mod-submission-list-item-1"]').should('not.exist') - - cy.get('[data-testid=mod-dashboard-leftnav-for-review]') - .click() - - cy.get('[data-testid="mod-submission-list-item-1"]').should('exist') - }) - - it('it shows the moderation top nav', () => { - cy.get('[data-testid="mod-submission-list-item-1"]').click() - cy.get('[data-testid="mod-edit-submission-copy-submission-id"]').click() - - // Check that the value copied to the clipboard is the same that's displayed. - const clipboardResult = cy.window().then(win => win.navigator.clipboard.readText()) - - // The timeout is to give time for the clipboard to be read. - clipboardResult.should('exist', 10000) - }) - - it('toggle between submissions and healthcare professionals submissions', () => { - cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('not.exist') - cy.get('[data-testid="mod-facility-list-item-1"]').should('not.exist') - cy.get('[data-testid="mod-submission-list-item-1').should('exist') - - cy.get('[data-testid="submission-type-select"]').select('FACILITIES') - cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('not.exist') - cy.get('[data-testid="mod-facility-list-item-1"]').should('exist') - cy.get('[data-testid="mod-submission-list-item-1').should('not.exist') - - cy.get('[data-testid="submission-type-select"]').select('HEALTHCARE_PROFESSIONALS') - cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('exist') - cy.get('[data-testid="mod-facility-list-item-1"]').should('not.exist') - cy.get('[data-testid="mod-submission-list-item-1').should('not.exist') - - cy.get('[data-testid="submission-type-select"]').select('SUBMISSIONS') - cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('not.exist') - cy.get('[data-testid="mod-facility-list-item-1"]').should('not.exist') - cy.get('[data-testid="mod-submission-list-item-1').should('exist') - }) +before(() => { + cy.login() +}) - after(() => { - Cypress.session.clearCurrentSessionData() - }) +describe('Moderation dashboard', () => { + before(() => { + cy.intercept('POST', '**/', req => { + aliasQuery(req, 'query Submissions', fakeSubmissionResponse) }) - } -) -describe('Moderation Edit Submission Form', () => { - before(function() { - cy.fixture(FAKE_SUBMISSION_RESPONSE_PATH).then(fakeSubmissionResponse => { - this.fakeSubmissionResponse = fakeSubmissionResponse - }) + cy.visit('/moderation') + cy.wait('@query Submissions') }) context('Landscape mode', () => { - before(function() { + beforeEach(() => { cy.viewport('macbook-16') - - cy.visit('/login') - Cypress.session.clearCurrentSessionData() - - // This intercepts the call to the graphQL api in order to use fake data in the tests to protect - // the real data. - cy.intercept('POST', '**/', req => { - if (req.body.query && req.body.query.includes('query Submissions')) { - req.reply({ - statusCode: 200, - body: this.fakeSubmissionResponse - }) - } else { - req.continue() - } - }).as('getSubmissions') - - cy.origin('https://findadoc.jp.auth0.com/', () => { - cy.get('input#username').should('be.visible').type('findadoctest@proton.me') - cy.get('[data-action-button-primary]').should('be.visible').click() - cy.get('input#password').should('be.visible').type('vCnL5J8agHg6m2f') - cy.get('[data-action-button-primary]').should('be.visible').click() - }) - - cy.url({ timeout: 10000 }).should('equal', 'http://localhost:3000/') - - /* Chaining of visit was used here to make sure the user was logged in and that it would - 100 percent visit moderation */ - cy.get('[data-testid=top-nav-mod-link]').click().visit('/moderation') - - cy.url({ timeout: 10000 }).should('include', '/moderation') - - cy.wait('@getSubmissions') - - cy.get('[data-testid="mod-submission-list-item-1"]').click() - }) - - after(() => { - Cypress.session.clearCurrentSessionData() - }) - - it('contains the following input fields', () => { - cy.get('[data-testid="submission-form-nameEn"]').should('exist') - cy.get('[data-testid="submission-form-nameJa"]').should('exist') - cy.get('[data-testid="submission-form-phone"]').should('exist') - cy.get('[data-testid="submission-form-email"]').should('exist') - cy.get('[data-testid="submission-form-website"]').should('exist') - cy.get('[data-testid="submission-form-postalCode"]').should('exist') - cy.get('[data-testid="submission-form-cityEn"]').should('exist') - cy.get('[data-testid="submission-form-addressLine1En"]').should('exist') - cy.get('[data-testid="submission-form-addressLine2En"]').should('exist') - cy.get('[data-testid=submission-form-cityJa]').should('exist') - cy.get('[data-testid="submission-form-addressLine1Ja"]').should('exist') - cy.get('[data-testid="submission-form-addressLine2Ja"]').should('exist') - cy.get('[data-testid="submission-form-google-maps"]').should('exist') - cy.get('[data-testid="submission-form-mapLatitude"]').should('exist') - cy.get('[data-testid="submission-form-mapLongitude"]').should('exist') - }) - - it('contains the following left nav buttons for navigation', () => { - cy.get('[data-testid="submission-form-leftnav-contact-information"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.contactInformation) - cy.get('[data-testid="submission-form-leftnav-addresses"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.addresses) - cy.get('[data-testid="submission-form-leftnav-google-maps-information"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.googleMapsInformation) - cy.get('[data-testid="submission-form-leftnav-healthcare-professional-ids"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.healthcareProfessionalIds) - cy.get('[data-testid="submission-form-leftnav-change-log"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.changeLog) - cy.get('[data-testid="submission-form-leftnav-healthcare-professional-name"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.healthcareProfessionalName) - cy.get('[data-testid="submission-form-leftnav-healthcare-professional-medical-info"]').should('exist') - .should('contain', enUS.modPanelSubmissionLeftNavbar.healthcareProfessionalMedicalInfo) - }) - - it('should contain the following select field', () => { - cy.get('[data-testid="submission-form-prefectureEn"]').should('exist') - cy.get('[data-testid="submission-form-prefectureJa"]').should('exist') - cy.get('[data-testid="submission-form-accepted-insurances"]').should('exist') - cy.get('[data-testid="submission-form-degrees"]').should('exist') - cy.get('[data-testid="submission-form-specialties"]').should('exist') - cy.get('[data-testid="submission-form-locales"]').should('exist') }) - it('should autofill the form', function() { - const submission = this.fakeSubmissionResponse.data.submissions[1].facility - - cy.get('[data-testid="submission-form-nameEn"]').find('input') - .should('have.value', submission.nameEn) - - cy.get('[data-testid="submission-form-nameJa"]').find('input') - .should('have.value', submission.nameJa) - - cy.get('[data-testid="submission-form-phone"]').find('input') - .should('have.value', submission.contact.phone) - }) - - it('should be able to type in all input fields', () => { - cy.get('[data-testid="submission-form-nameEn"]').find('input').type('Hospital') - cy.get('[data-testid="submission-form-nameJa"]').find('input').type('立川中央病院') - cy.get('[data-testid="submission-form-phone"]').find('input').type('08080939393') - cy.get('[data-testid="submission-form-email"]').find('input').type('example@mail.com') - cy.get('[data-testid="submission-form-website"]').find('input').type('http://example.com') - cy.get('[data-testid="submission-form-postalCode"]').find('input').type('180-0000') - cy.get('[data-testid="submission-form-cityEn"]').find('input').type('Shibuya') - cy.get('[data-testid="submission-form-addressLine1En"]').find('input').type('some address line 1') - cy.get('[data-testid="submission-form-addressLine2En"]').find('input').type('some address line 2') - cy.get('[data-testid=submission-form-cityJa]').find('input').type('渋谷区') - cy.get('[data-testid="submission-form-addressLine1Ja"]').find('input').type('道の駅') - cy.get('[data-testid="submission-form-addressLine2Ja"]').find('input').type('道の') - cy.get('[data-testid="submission-form-google-maps"]') - .find('input') - .type('www.google.com/maps/place/82+Yamatech%C5%8D,+Naka+Ward,+Yokohama' - + ',+Kanagawa+231-0862,+Japan/@35.437123,139.651471,16z/data=!4m6!3m5!1s' - + '0x60185d201648e7c1:0x8f37d37bb381e29!8m2!3d35.4371228!4d139.6514712!16s%2Fg%2F11clpxxvx5?hl=en-US&entry=ttu') - cy.get('[data-testid="submission-form-mapLatitude"]').find('input').type('35.437123') - cy.get('[data-testid="submission-form-mapLongitude"]').find('input').type('139.651471') - }) - - it('should be able to select a field', () => { - cy.get('[data-testid="submission-form-prefectureEn"]').select('Hokkaido') - cy.get('[data-testid="submission-form-prefectureJa"]').select('北海道') - }) - - it('should be display error messages', () => { - cy.get('[data-testid="submission-form-nameEn"]').find('input').clear().type('立川中央病院').realPress('Tab') - cy.get('[data-testid="submission-form-nameEn"]') - .find('p').should('exist').contains('Invalid English Name') - - cy.get('[data-testid="submission-form-nameJa"]').find('input').clear().type('Tachikawa Hospital').realPress('Tab') - cy.get('[data-testid="submission-form-nameJa"]') - .find('p').should('exist').contains('Invalid Japanese Name') - - cy.get('[data-testid="submission-form-phone"]').find('input').clear().type('Hello').realPress('Tab') - cy.get('[data-testid="submission-form-phone"]') - .find('p').should('exist').contains('Invalid Phone Number') - - cy.get('[data-testid="submission-form-email"]').find('input').clear().type('example').realPress('Tab') - cy.get('[data-testid="submission-form-email"]') - .find('p').should('exist').contains('Invalid Email Address') - - cy.get('[data-testid="submission-form-website"]').find('input').clear().type('example').realPress('Tab') - cy.get('[data-testid="submission-form-website"]') - .find('p').should('exist').contains('Invalid Website URL') - - cy.get('[data-testid="submission-form-postalCode"]').find('input').clear().type('180-0').realPress('Tab') - cy.get('[data-testid="submission-form-postalCode"]') - .find('p').should('exist').contains('Invalid Postal Code') - - cy.get('[data-testid="submission-form-cityEn"]').find('input').clear().type('渋谷区').realPress('Tab') - cy.get('[data-testid="submission-form-cityEn"]') - .find('p').should('exist').contains('Invalid English City Name') - - cy.get('[data-testid="submission-form-addressLine1En"]').find('input').clear().type('道の駅').realPress('Tab') - cy.get('[data-testid="submission-form-addressLine1En"]') - .find('p').should('exist').contains('Invalid English Address') - - cy.get('[data-testid="submission-form-addressLine2En"]').find('input').clear().type('道の駅').realPress('Tab') - cy.get('[data-testid="submission-form-addressLine2En"]') - .find('p').should('exist').contains('Invalid English Address') - - cy.get('[data-testid=submission-form-cityJa]').find('input').clear().type('Shibuya').realPress('Tab') - cy.get('[data-testid=submission-form-cityJa]') - .find('p').should('exist').contains('Invalid Japanese City Name') - - cy.get('[data-testid="submission-form-addressLine1Ja"]') - .find('input').clear().type('Peanutbutter street').realPress('Tab') - cy.get('[data-testid="submission-form-addressLine1Ja"]').should('exist').contains('Invalid Japanese Address') - - cy.get('[data-testid="submission-form-addressLine2Ja"]').find('input').clear().type('Jelly street').realPress('Tab') - cy.get('[data-testid="submission-form-addressLine2Ja"]').should('exist').contains('Invalid Japanese Address') - - cy.get('[data-testid="submission-form-mapLatitude"]') - .find('input').clear().type('Not Number Latitude').realPress('Tab') - cy.get('[data-testid="submission-form-mapLatitude"]') - .find('p').should('exist').contains('Invalid Latitude') - - cy.get('[data-testid="submission-form-mapLongitude"]') - .find('input').clear().type('Not Number Longitude').realPress('Tab') - cy.get('[data-testid="submission-form-mapLongitude"]') - .find('p').should('exist').contains('Invalid Longitude') - }) - }) -}) - -describe('Moderation Edit Submission Modal', () => { - before(function() { - cy.fixture(FAKE_SUBMISSION_RESPONSE_PATH).then(fakeSubmissionResponse => { - console.log(fakeSubmissionResponse) - this.fakeSubmissionResponse = fakeSubmissionResponse - }) - }) - - context('Landscape mode', () => { - before(function() { - cy.viewport('macbook-16') - - cy.visit('/login') - Cypress.session.clearCurrentSessionData() - - // This intercepts the call to the graphQL api in order to use fake data in the tests to protect - // the real data. - cy.intercept('POST', '**/', req => { - if (req.body.query && req.body.query.includes('query Submissions')) { - req.reply({ - statusCode: 200, - body: this.fakeSubmissionResponse - }) - } else { - req.continue() - } - }).as('getSubmissions') - - cy.origin('https://findadoc.jp.auth0.com/', () => { - cy.get('input#username').should('be.visible').type('findadoctest@proton.me') - cy.get('[data-action-button-primary]').should('be.visible').click() - cy.get('input#password').should('be.visible').type('vCnL5J8agHg6m2f') - cy.get('[data-action-button-primary]').should('be.visible').click() - }) - - cy.url({ timeout: 10000 }).should('equal', 'http://localhost:3000/') - - cy.get('[data-testid=top-nav-mod-link]').click().visit('/moderation') - - cy.url({ timeout: 10000 }).should('include', '/moderation') - - cy.wait('@getSubmissions') - + it('shows mod dashboard left navbar buttons with correct counts and functionality', () => { + // The number for include text is for the status in the fake data. + cy.get('[data-testid=mod-dashboard-leftnav-for-review]') + .should('exist') + .should( + 'include.text', + enUS.modDashboardLeftNav.forReview + ) + .should( + 'include.text', + '1' + ) + + cy.get('[data-testid=mod-dashboard-leftnav-approved]') + .should('exist') + .should( + 'include.text', + enUS.modDashboardLeftNav.approved + ) + .should( + 'include.text', + '1' + ) + + cy.get('[data-testid=mod-dashboard-leftnav-rejected]') + .should('exist') + .should( + 'include.text', + enUS.modDashboardLeftNav.rejected + ) + .should( + 'include.text', + '0' + ) + + cy.get('[data-testid="mod-submission-list-item-1"]').should('exist') + + cy.get('[data-testid=mod-dashboard-leftnav-approved]') + .click() + + cy.get('[data-testid="mod-submission-list-item-1"]').should('exist') + + cy.get('[data-testid=mod-dashboard-leftnav-rejected]') + .click() + + cy.get('[data-testid="mod-submission-list-item-1"]').should('not.exist') + + cy.get('[data-testid=mod-dashboard-leftnav-for-review]') + .click() + + cy.get('[data-testid="mod-submission-list-item-1"]').should('exist') + }) + + it('toggle between submissions and healthcare professionals submissions', () => { + cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('not.exist') + cy.get('[data-testid="mod-facility-list-item-1"]').should('not.exist') + cy.get('[data-testid="mod-submission-list-item-1').should('exist') + + cy.get('[data-testid="submission-type-select"]').select('FACILITIES') + cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('not.exist') + cy.get('[data-testid="mod-facility-list-item-1"]').should('exist') + cy.get('[data-testid="mod-submission-list-item-1').should('not.exist') + + cy.get('[data-testid="submission-type-select"]').select('HEALTHCARE_PROFESSIONALS') + cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('exist') + cy.get('[data-testid="mod-facility-list-item-1"]').should('not.exist') + cy.get('[data-testid="mod-submission-list-item-1').should('not.exist') + + cy.get('[data-testid="submission-type-select"]').select('SUBMISSIONS') + cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').should('not.exist') + cy.get('[data-testid="mod-facility-list-item-1"]').should('not.exist') + cy.get('[data-testid="mod-submission-list-item-1').should('exist') + }) + + it('it shows the moderation top nav', () => { cy.get('[data-testid="mod-submission-list-item-1"]').click() - }) + cy.get('[data-testid="mod-edit-submission-copy-submission-id"]').click() - after(() => { - Cypress.session.clearCurrentSessionData() - }) + // Check that the value copied to the clipboard is the same that's displayed. + const clipboardResult = cy.window().then(win => win.navigator.clipboard.readText()) - it('should display modal if user navigates back with unsaved changes', () => { - // When the user clicks the back button on their browser with unsaved changes... - cy.get('[data-testid="submission-form-nameEn"]').find('input').type('Hospital') - cy.go('back') - // ...the modal with the confirmation button should be visible. - cy.get('[data-testid="submission-form-modal"]').should('be.visible') - // When the user clicks the confirmation button on the modal... - cy.get('[data-testid="submission-form-modal-confirmation-btn"]').click() - // ...they should be navigated back to the moderation screen. - cy.location('pathname').should('eq', '/moderation') - }) - - it('should not display modal if user navigates back without making changes', () => { - cy.get('[data-testid="mod-submission-list-item-1"]').click() - // When the user clicks the back button on their browser without making changes... - cy.go('back') - // ...the modal with the confirmation button should not exist... - cy.get('[data-testid="submission-form-modal"]').should('not.exist') - // ... and they should be navigated back to the moderation screen. - cy.location('pathname').should('eq', '/moderation') + // The timeout is to give time for the clipboard to be read. + clipboardResult.should('exist', 10000) }) }) }) diff --git a/test/cypress/e2e/moderationEditFacility.cy.ts b/test/cypress/e2e/moderationEditFacility.cy.ts index 018dfbe6..80b1eb07 100644 --- a/test/cypress/e2e/moderationEditFacility.cy.ts +++ b/test/cypress/e2e/moderationEditFacility.cy.ts @@ -1,68 +1,37 @@ -import 'cypress-real-events' -import 'cypress-plugin-tab' -import facilityData from '../../fake_data/moderation_dashboard/fakeModFacilityData.json' import enUS from '../../../i18n/locales/en.json' +import fakeFacilityResult from '../../fake_data/moderation_dashboard/fakeModFacilityData.json' +import { aliasQuery } from '../utils' -const FAKE_FACILITY_RESPONSE_PATH = 'moderation_dashboard/fakeModFacilityData.json' +before(() => { + cy.login() +}) describe('Moderation edit facility form', () => { context('Landscape mode', () => { - // Before starting the execution, we need to load the fixture data using "this" context of the - // test object. We need to use regular function() callbacks instead of arrow functions when - // we need to access the "this" context. - before(function() { - cy.fixture(FAKE_FACILITY_RESPONSE_PATH).then(fakeFacilityResult => { - this.fakeFacilityResult = fakeFacilityResult - }) - - cy.viewport('macbook-16') - cy.visit('/login') - Cypress.session.clearCurrentSessionData() - - // This intercepts the call to the GraphQL API in order to use fake data in the tests to protect the real data. + before(() => { cy.intercept('POST', '**/', req => { - if (req.body.query && req.body.query.includes('query Facilities')) { - req.reply({ - statusCode: 200, - body: this.fakeFacilityResult - }) - } else { - req.continue() - } - }).as('getFacilities') - - cy.origin('https://findadoc.jp.auth0.com/', () => { - cy.get('input#username').should('be.visible').type('findadoctest@proton.me') - cy.get('[data-action-button-primary]').should('be.visible').click() - cy.get('input#password').should('be.visible').type('vCnL5J8agHg6m2f') - cy.get('[data-action-button-primary]').should('be.visible').click() + aliasQuery(req, 'query Facilities', fakeFacilityResult) }) - cy.url({ timeout: 10000 }).should('equal', 'http://localhost:3000/') + cy.visit('/moderation') - /* Chaining of visit was used here to make sure the user was logged in and that it would - 100 percent visit moderation */ - cy.get('[data-testid=top-nav-mod-link]').click().visit('/moderation') + cy.wait('@query Facilities') - cy.url({ timeout: 10000 }).should('include', '/moderation') - - cy.wait('@getFacilities') cy.get('[data-testid="submission-type-select"]').select('FACILITIES') - cy.get('[data-testid="mod-facility-list-item-1"]').click() }) - after(() => { - Cypress.session.clearCurrentSessionData() + beforeEach(() => { + cy.viewport('macbook-16') }) - it('contains the following fields and buttons in the topbar'), () => { + it('contains the following fields and buttons in the topbar', () => { cy.get('[data-testid="mod-edit-facility-hp-topbar-update"]').should('exist') .contains(enUS.modEditFacilityOrHPTopbar.updateAndExit) cy.get('[data-testid="mod-edit-facility-hp-topbar-delete"]').should('exist') .contains(enUS.modEditFacilityOrHPTopbar.delete) cy.get('[data-testid="mod-edit-facility-hp-topbar-copy-id"]').should('exist') - } + }) it('it copies the selected id', () => { cy.get('[data-testid="mod-edit-facility-hp-topbar-copy-id"]').click() @@ -100,35 +69,35 @@ describe('Moderation edit facility form', () => { it('should autofill all the form fields for an existing facility', () => { cy.get('[data-testid="mod-facility-section-nameEn"]') - .find('input').should('have.value', facilityData.data.facilities[0].nameEn) + .find('input').should('have.value', fakeFacilityResult.data.facilities[0].nameEn) cy.get('[data-testid="mod-facility-section-nameJa"]') - .find('input').should('have.value', facilityData.data.facilities[0].nameJa) + .find('input').should('have.value', fakeFacilityResult.data.facilities[0].nameJa) cy.get('[data-testid="mod-facility-section-phone"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.phone) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.phone) cy.get('[data-testid="mod-facility-section-email"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.email) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.email) cy.get('[data-testid="mod-facility-section-website"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.website) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.website) cy.get('[data-testid="mod-facility-section-postalCode"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.postalCode) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.postalCode) cy.get('[data-testid="mod-facility-section-cityEn"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.cityEn) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.cityEn) cy.get('[data-testid="mod-facility-section-addressLine1En"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.addressLine1En) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.addressLine1En) cy.get('[data-testid="mod-facility-section-addressLine2En"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.addressLine2En) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.addressLine2En) cy.get('[data-testid="mod-facility-section-cityJa"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.cityJa) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.cityJa) cy.get('[data-testid="mod-facility-section-addressLine1Ja"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.addressLine1Ja) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.addressLine1Ja) cy.get('[data-testid="mod-facility-section-addressLine2Ja"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.address.addressLine2Ja) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.address.addressLine2Ja) cy.get('[data-testid="mod-facility-section-google-maps"]').find('input') - .should('have.value', facilityData.data.facilities[0].contact.googleMapsUrl) + .should('have.value', fakeFacilityResult.data.facilities[0].contact.googleMapsUrl) cy.get('[data-testid="mod-facility-section-mapLatitude"]').find('input') - .should('have.value', facilityData.data.facilities[0].mapLatitude) + .should('have.value', fakeFacilityResult.data.facilities[0].mapLatitude) cy.get('[data-testid="mod-facility-section-mapLongitude"]').find('input') - .should('have.value', facilityData.data.facilities[0].mapLongitude) + .should('have.value', fakeFacilityResult.data.facilities[0].mapLongitude) }) it('should be able to type in all input fields', () => { diff --git a/test/cypress/e2e/moderationEditHealthcareProfessional.cy.ts b/test/cypress/e2e/moderationEditHealthcareProfessional.cy.ts index 9f203387..0279504e 100644 --- a/test/cypress/e2e/moderationEditHealthcareProfessional.cy.ts +++ b/test/cypress/e2e/moderationEditHealthcareProfessional.cy.ts @@ -1,59 +1,29 @@ -import 'cypress-real-events' -import 'cypress-plugin-tab' +import fakeHealthcareProfessionalResult from '../../fake_data/moderation_dashboard/fakeModHealthcareProfessionalData.json' +import { aliasQuery } from '../utils' -const FAKE_HEALTHCARE_PROFESSIONAL_RESPONSE_PATH = 'moderation_dashboard/fakeModHealthcareprofessionalData.json' -const AUTH_URL = 'https://findadoc.jp.auth0.com/' -const SITE_URL = 'http://localhost:3000/' +before(() => { + cy.login() +}) describe('Moderation edit professional healthcare form', () => { context('Landscape mode', () => { - // Before starting the execution, we need to load the fixture data using "this" context of the - // test object. We need to use regular function() callbacks instead of arrow functions when - // we need to access the "this" context. - before(function() { - cy.fixture(FAKE_HEALTHCARE_PROFESSIONAL_RESPONSE_PATH).then(fakeHealthcareprofessionalResult => { - this.fakeHealthcareprofessionalResult = fakeHealthcareprofessionalResult - }) - + before(() => { cy.viewport('macbook-16') - cy.visit('/login', { timeout: 10000 }) - Cypress.session.clearCurrentSessionData() - // This intercepts the call to the GraphQL API in order to use fake data in the tests to protect the real data. cy.intercept('POST', '**/', req => { - if (req.body.query && req.body.query.includes('query HealthcareProfessionals')) { - req.reply({ - statusCode: 200, - body: this.fakeHealthcareprofessionalResult - }) - } else { - req.continue() - } - }).as('getHealthcareProfessionals') - - cy.origin(AUTH_URL, () => { - cy.get('input#username').should('be.visible').type('findadoctest@proton.me') - cy.get('[data-action-button-primary]').should('be.visible').click() - cy.get('input#password').should('be.visible').type('vCnL5J8agHg6m2f') - cy.get('[data-action-button-primary]').should('be.visible').click() + aliasQuery(req, 'HealthcareProfessionalSearchFilters', fakeHealthcareProfessionalResult) }) - cy.url({ timeout: 10000 }).should('equal', SITE_URL) + cy.visit('/moderation') - /* Chaining of visit was used here to make sure the user was logged in and that it would - 100 percent visit moderation */ - cy.get('[data-testid=top-nav-mod-link]', { timeout: 10000 }).click().visit('/moderation') + cy.wait('@HealthcareProfessionalSearchFilters') - cy.url({ timeout: 10000 }).should('include', '/moderation') - - cy.wait('@getHealthcareProfessionals', { timeout: 10000 }) - cy.get('[data-testid="submission-type-select"]', { timeout: 10000 }).select('HEALTHCARE_PROFESSIONALS') - - cy.get('[data-testid="mod-healthcare-professional-list-item-1"]', { timeout: 10000 }).click() + cy.get('[data-testid="submission-type-select"]').select('HEALTHCARE_PROFESSIONALS') + cy.get('[data-testid="mod-healthcare-professional-list-item-1"]').click() }) - after(() => { - Cypress.session.clearCurrentSessionData() + beforeEach(() => { + cy.viewport('macbook-16') }) it('contains the following input and select fields', () => { diff --git a/test/cypress/e2e/moderationEditSubmissionForm.cy.ts b/test/cypress/e2e/moderationEditSubmissionForm.cy.ts new file mode 100644 index 00000000..c777c395 --- /dev/null +++ b/test/cypress/e2e/moderationEditSubmissionForm.cy.ts @@ -0,0 +1,168 @@ +import enUS from '../../../i18n/locales/en.json' +import { aliasQuery } from '../utils' +import fakeSubmissionResponse from '../../fake_data/moderation_dashboard/fakeSubmissionResponse.json' + +before(() => { + cy.login() +}) + +describe('Moderation Edit Submission Form', () => { + context('Landscape mode', () => { + before(() => { + cy.intercept('POST', '**/', req => { + aliasQuery(req, 'query Submissions', fakeSubmissionResponse) + }) + + cy.visit('/moderation') + cy.wait('@query Submissions') + cy.get('[data-testid="mod-submission-list-item-1"]').click() + }) + + beforeEach(() => { + cy.viewport('macbook-16') + }) + + it('contains the following input fields', () => { + cy.get('[data-testid="submission-form-nameEn"]').should('exist') + cy.get('[data-testid="submission-form-nameJa"]').should('exist') + cy.get('[data-testid="submission-form-phone"]').should('exist') + cy.get('[data-testid="submission-form-email"]').should('exist') + cy.get('[data-testid="submission-form-website"]').should('exist') + cy.get('[data-testid="submission-form-postalCode"]').should('exist') + cy.get('[data-testid="submission-form-cityEn"]').should('exist') + cy.get('[data-testid="submission-form-addressLine1En"]').should('exist') + cy.get('[data-testid="submission-form-addressLine2En"]').should('exist') + cy.get('[data-testid=submission-form-cityJa]').should('exist') + cy.get('[data-testid="submission-form-addressLine1Ja"]').should('exist') + cy.get('[data-testid="submission-form-addressLine2Ja"]').should('exist') + cy.get('[data-testid="submission-form-google-maps"]').should('exist') + cy.get('[data-testid="submission-form-mapLatitude"]').should('exist') + cy.get('[data-testid="submission-form-mapLongitude"]').should('exist') + }) + + it('contains the following left nav buttons for navigation', () => { + cy.get('[data-testid="submission-form-leftnav-contact-information"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.contactInformation) + cy.get('[data-testid="submission-form-leftnav-addresses"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.addresses) + cy.get('[data-testid="submission-form-leftnav-google-maps-information"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.googleMapsInformation) + cy.get('[data-testid="submission-form-leftnav-healthcare-professional-ids"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.healthcareProfessionalIds) + cy.get('[data-testid="submission-form-leftnav-change-log"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.changeLog) + cy.get('[data-testid="submission-form-leftnav-healthcare-professional-name"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.healthcareProfessionalName) + cy.get('[data-testid="submission-form-leftnav-healthcare-professional-medical-info"]').should('exist') + .should('contain', enUS.modPanelSubmissionLeftNavbar.healthcareProfessionalMedicalInfo) + }) + + it('should contain the following select field', () => { + cy.get('[data-testid="submission-form-prefectureEn"]').should('exist') + cy.get('[data-testid="submission-form-prefectureJa"]').should('exist') + cy.get('[data-testid="submission-form-accepted-insurances"]').should('exist') + cy.get('[data-testid="submission-form-degrees"]').should('exist') + cy.get('[data-testid="submission-form-specialties"]').should('exist') + cy.get('[data-testid="submission-form-locales"]').should('exist') + }) + + it('should autofill the form', () => { + const submission = fakeSubmissionResponse.data.submissions[1].facility + + cy.get('[data-testid="submission-form-nameEn"]').find('input') + .should('have.value', submission.nameEn) + + cy.get('[data-testid="submission-form-nameJa"]').find('input') + .should('have.value', submission.nameJa) + + cy.get('[data-testid="submission-form-phone"]').find('input') + .should('have.value', submission.contact.phone) + }) + + it('should be able to type in all input fields', () => { + cy.get('[data-testid="submission-form-nameEn"]').find('input').type('Hospital') + cy.get('[data-testid="submission-form-nameJa"]').find('input').type('立川中央病院') + cy.get('[data-testid="submission-form-phone"]').find('input').type('08080939393') + cy.get('[data-testid="submission-form-email"]').find('input').type('example@mail.com') + cy.get('[data-testid="submission-form-website"]').find('input').type('http://example.com') + cy.get('[data-testid="submission-form-postalCode"]').find('input').type('180-0000') + cy.get('[data-testid="submission-form-cityEn"]').find('input').type('Shibuya') + cy.get('[data-testid="submission-form-addressLine1En"]').find('input').type('some address line 1') + cy.get('[data-testid="submission-form-addressLine2En"]').find('input').type('some address line 2') + cy.get('[data-testid=submission-form-cityJa]').find('input').type('渋谷区') + cy.get('[data-testid="submission-form-addressLine1Ja"]').find('input').type('道の駅') + cy.get('[data-testid="submission-form-addressLine2Ja"]').find('input').type('道の') + cy.get('[data-testid="submission-form-google-maps"]') + .find('input') + .type('www.google.com/maps/place/82+Yamatech%C5%8D,+Naka+Ward,+Yokohama' + + ',+Kanagawa+231-0862,+Japan/@35.437123,139.651471,16z/data=!4m6!3m5!1s' + + '0x60185d201648e7c1:0x8f37d37bb381e29!8m2!3d35.4371228!4d139.6514712!16s%2Fg%2F11clpxxvx5?hl=en-US&entry=ttu') + cy.get('[data-testid="submission-form-mapLatitude"]').find('input').type('35.437123') + cy.get('[data-testid="submission-form-mapLongitude"]').find('input').type('139.651471') + }) + + it('should be able to select a field', () => { + cy.get('[data-testid="submission-form-prefectureEn"]').select('Hokkaido') + cy.get('[data-testid="submission-form-prefectureJa"]').select('北海道') + }) + + it('should be display error messages', () => { + cy.get('[data-testid="submission-form-nameEn"]').find('input').clear().type('立川中央病院').realPress('Tab') + cy.get('[data-testid="submission-form-nameEn"]') + .find('p').should('exist').contains('Invalid English Name') + + cy.get('[data-testid="submission-form-nameJa"]').find('input').clear().type('Tachikawa Hospital').realPress('Tab') + cy.get('[data-testid="submission-form-nameJa"]') + .find('p').should('exist').contains('Invalid Japanese Name') + + cy.get('[data-testid="submission-form-phone"]').find('input').clear().type('Hello').realPress('Tab') + cy.get('[data-testid="submission-form-phone"]') + .find('p').should('exist').contains('Invalid Phone Number') + + cy.get('[data-testid="submission-form-email"]').find('input').clear().type('example').realPress('Tab') + cy.get('[data-testid="submission-form-email"]') + .find('p').should('exist').contains('Invalid Email Address') + + cy.get('[data-testid="submission-form-website"]').find('input').clear().type('example').realPress('Tab') + cy.get('[data-testid="submission-form-website"]') + .find('p').should('exist').contains('Invalid Website URL') + + cy.get('[data-testid="submission-form-postalCode"]').find('input').clear().type('180-0').realPress('Tab') + cy.get('[data-testid="submission-form-postalCode"]') + .find('p').should('exist').contains('Invalid Postal Code') + + cy.get('[data-testid="submission-form-cityEn"]').find('input').clear().type('渋谷区').realPress('Tab') + cy.get('[data-testid="submission-form-cityEn"]') + .find('p').should('exist').contains('Invalid English City Name') + + cy.get('[data-testid="submission-form-addressLine1En"]').find('input').clear().type('道の駅').realPress('Tab') + cy.get('[data-testid="submission-form-addressLine1En"]') + .find('p').should('exist').contains('Invalid English Address') + + cy.get('[data-testid="submission-form-addressLine2En"]').find('input').clear().type('道の駅').realPress('Tab') + cy.get('[data-testid="submission-form-addressLine2En"]') + .find('p').should('exist').contains('Invalid English Address') + + cy.get('[data-testid=submission-form-cityJa]').find('input').clear().type('Shibuya').realPress('Tab') + cy.get('[data-testid=submission-form-cityJa]') + .find('p').should('exist').contains('Invalid Japanese City Name') + + cy.get('[data-testid="submission-form-addressLine1Ja"]') + .find('input').clear().type('Peanutbutter street').realPress('Tab') + cy.get('[data-testid="submission-form-addressLine1Ja"]').should('exist').contains('Invalid Japanese Address') + + cy.get('[data-testid="submission-form-addressLine2Ja"]').find('input').clear().type('Jelly street').realPress('Tab') + cy.get('[data-testid="submission-form-addressLine2Ja"]').should('exist').contains('Invalid Japanese Address') + + cy.get('[data-testid="submission-form-mapLatitude"]') + .find('input').clear().type('Not Number Latitude').realPress('Tab') + cy.get('[data-testid="submission-form-mapLatitude"]') + .find('p').should('exist').contains('Invalid Latitude') + + cy.get('[data-testid="submission-form-mapLongitude"]') + .find('input').clear().type('Not Number Longitude').realPress('Tab') + cy.get('[data-testid="submission-form-mapLongitude"]') + .find('p').should('exist').contains('Invalid Longitude') + }) + }) +}) diff --git a/test/cypress/e2e/moderationEditSubmissionModal.cy.ts b/test/cypress/e2e/moderationEditSubmissionModal.cy.ts new file mode 100644 index 00000000..508232c6 --- /dev/null +++ b/test/cypress/e2e/moderationEditSubmissionModal.cy.ts @@ -0,0 +1,46 @@ +import fakeSubmissionResponse from '../../fake_data/moderation_dashboard/fakeSubmissionResponse.json' +import { aliasQuery } from '../utils' + +before(() => { + cy.login() +}) + +describe('Moderation Edit Submission Modal', () => { + before(() => { + cy.viewport('macbook-16') + + cy.intercept('POST', '**/', req => { + aliasQuery(req, 'query Submissions', fakeSubmissionResponse) + }) + + cy.visit('/moderation') + cy.wait('@query Submissions') + }) + + context('Landscape mode', () => { + it('should display modal if user navigates back with unsaved changes', () => { + cy.get('[data-testid="mod-submission-list-item-1"]').click() + + // When the user clicks the back button on their browser with unsaved changes... + cy.get('[data-testid="submission-form-nameEn"]').find('input').type('Hospital') + cy.go('back') + // ...the modal with the confirmation button should be visible. + cy.get('[data-testid="submission-form-modal"]').should('be.visible') + // When the user clicks the confirmation button on the modal... + cy.get('[data-testid="submission-form-modal-confirmation-btn"]').click() + // ...they should be navigated back to the moderation screen. + cy.location('pathname').should('eq', '/moderation') + }) + + it('should not display modal if user navigates back without making changes', () => { + cy.get('[data-testid="mod-submission-list-item-1"]').click() + + // When the user clicks the back button on their browser without making changes... + cy.go('back') + // ...the modal with the confirmation button should not exist... + cy.get('[data-testid="submission-form-modal"]').should('not.exist') + // ... and they should be navigated back to the moderation screen. + cy.location('pathname').should('eq', '/moderation') + }) + }) +}) diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js deleted file mode 100644 index 119ab03f..00000000 --- a/test/cypress/support/commands.js +++ /dev/null @@ -1,25 +0,0 @@ -// *********************************************** -// 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) => { ... }) diff --git a/test/cypress/support/commands.ts b/test/cypress/support/commands.ts new file mode 100644 index 00000000..f01828dd --- /dev/null +++ b/test/cypress/support/commands.ts @@ -0,0 +1,54 @@ +// *********************************************** +// 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) => { ... }) + +// https://docs.cypress.io/app/guides/authentication-testing/auth0-authentication +import { auth0Login } from '../utils' + +Cypress.Commands.add('login', () => { + const auth0UserName = Cypress.env('AUTH0_USERNAME') + + const log = Cypress.log({ + displayName: 'AUTH0_LOGIN', + message: [`🔐 Authenticating | ${auth0UserName}`], + autoEnd: false + }) + + log.snapshot('Connecting to auth0') + + cy.session(`auth0-${auth0UserName}`, () => { + auth0Login() + }, { + validate: () => { + // https://auth0.com/docs/manage-users/cookies/authentication-api-cookies + // https://docs.cypress.io/api/commands/getcookie + cy.getCookie('auth0', { domain: 'findadoc.jp.auth0.com' }).should('exist') + }, + cacheAcrossSpecs: true + }) + + log.snapshot('Successfully logged in') + log.end() +}) diff --git a/test/cypress/support/e2e.js b/test/cypress/support/e2e.ts similarity index 64% rename from test/cypress/support/e2e.js rename to test/cypress/support/e2e.ts index d1dd1353..c099f1bd 100644 --- a/test/cypress/support/e2e.js +++ b/test/cypress/support/e2e.ts @@ -18,3 +18,15 @@ import './commands' // Alternatively you can use CommonJS syntax: // require('./commands') + +// https://github.com/dmtrKovalenko/cypress-real-events#readme +import 'cypress-real-events' + +// https://www.npmjs.com/package/cypress-plugin-tab +import 'cypress-plugin-tab' + +// https://docs.cypress.io/api/cypress-api/screenshot-api +// ! Be careful ! Enabling --record (Test Replay) hides the runner UI ! +Cypress.Screenshot.defaults({ + capture: 'runner' +}) diff --git a/test/cypress/support/index.d.ts b/test/cypress/support/index.d.ts index 9a1fd9b5..196e1116 100644 --- a/test/cypress/support/index.d.ts +++ b/test/cypress/support/index.d.ts @@ -1,10 +1,8 @@ -export { } +/// -declare global { - namespace Cypress { - interface Chainable { - isInViewport(args?: string): Chainable> - isNotInViewport(args?: string): Chainable> - } +// https://docs.cypress.io/app/tooling/typescript-support#Types-for-Custom-Commands +declare namespace Cypress{ + interface Chainable { + login(): Chainable } } diff --git a/test/cypress/tsconfig.json b/test/cypress/tsconfig.json index 62c9e373..31f6cae9 100644 --- a/test/cypress/tsconfig.json +++ b/test/cypress/tsconfig.json @@ -7,7 +7,9 @@ ], "types": [ "cypress", - "node" + "node", + "cypress-real-events", + "cypress-plugin-tab" ], "sourceMap": false, "resolveJsonModule": true, diff --git a/test/cypress/utils.ts b/test/cypress/utils.ts new file mode 100644 index 00000000..be392ee9 --- /dev/null +++ b/test/cypress/utils.ts @@ -0,0 +1,69 @@ +import type { CyHttpMessages } from 'cypress/types/net-stubbing' + +export type IncomingHttpRequest = CyHttpMessages.IncomingHttpRequest + +// https://docs.cypress.io/app/guides/authentication-testing/auth0-authentication +export const auth0Login = () => { + cy.visit('/login') + cy.origin(Cypress.env('AUTH0_DOMAIN'), () => { + const auth0UserName = Cypress.env('AUTH0_USERNAME') + const auth0Password = Cypress.env('AUTH0_PASSWORD') + + cy.get('input#username').type(auth0UserName) + cy.get('[data-action-button-primary]').click() + cy.get('input#password').type(auth0Password) + cy.get('[data-action-button-primary]').click() + }) + + const baseUrl = `${Cypress.config().baseUrl}/` + + cy.url().should('equal', baseUrl) +} + +/** + * https://docs.cypress.io/app/guides/network-requests#Working-with-GraphQL + * + * Set the alias for a request based on the operation name. After setting it we can use cy.wait(@{alias}). + * + * e.g. operation: query Submissions / cy.wait('@query Submissions') + **/ +export const aliasQuery = (req: IncomingHttpRequest, operation: string, responseBody: unknown) => { + /** + * Check if the GraphQL operation is included in the request body + **/ + const hasOperation = (req: IncomingHttpRequest, operation: string): boolean => { + const { query } = req.body + return query && query.includes(operation) + } + + /** + * https://docs.cypress.io/api/commands/intercept#Interception-lifecycle + * + * https://docs.cypress.io/api/commands/intercept#cyintercept-and-request-caching + * + * Sometimes Cypress.intercept() cannot intercept a request due to the request being cached and not hitting the + * network layer (where Cypress.intercept() works). + * + * To prevent this, we set 'cache-control' to 'no-store', this prevents all the requests to be cached. + **/ + const preventRequestCache = (req: IncomingHttpRequest) => { + req.on('before:response', res => { + // force all API responses to not be cached + res.headers['cache-control'] = 'no-store' + }) + } + + preventRequestCache(req) + + if (!hasOperation(req, operation)) { + req.continue() + return + } + + req.alias = operation + + req.reply({ + statusCode: 200, + body: responseBody + }) +} diff --git a/test/fake_data/moderation_dashboard/fakeModHealthcareprofessionalData.json b/test/fake_data/moderation_dashboard/fakeModHealthcareProfessionalData.json similarity index 95% rename from test/fake_data/moderation_dashboard/fakeModHealthcareprofessionalData.json rename to test/fake_data/moderation_dashboard/fakeModHealthcareProfessionalData.json index 572630d2..4c832f05 100644 --- a/test/fake_data/moderation_dashboard/fakeModHealthcareprofessionalData.json +++ b/test/fake_data/moderation_dashboard/fakeModHealthcareProfessionalData.json @@ -1,6 +1,6 @@ { "data": { - "healthcareprofessionals": [ + "healthcareProfessionals": [ { "acceptedInsurance": [ "UNINSURED" diff --git a/yarn.lock b/yarn.lock index 246260ee..2b3a1a44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7243,6 +7243,17 @@ __metadata: languageName: node linkType: hard +"cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10/0d52657d7ae36eb130999dffff1168ec348687b48dd38e2ff59992ed916c88d328cf1d07ff4a4a10bc78de5e1c23f04b306d569e42f7a2293915c081e4dfee86 + languageName: node + linkType: hard + "crossws@npm:^0.2.0, crossws@npm:^0.2.4": version: 0.2.4 resolution: "crossws@npm:0.2.4" @@ -7895,6 +7906,27 @@ __metadata: languageName: node linkType: hard +"dotenv-cli@npm:^8.0.0": + version: 8.0.0 + resolution: "dotenv-cli@npm:8.0.0" + dependencies: + cross-spawn: "npm:^7.0.6" + dotenv: "npm:^16.3.0" + dotenv-expand: "npm:^10.0.0" + minimist: "npm:^1.2.6" + bin: + dotenv: cli.js + checksum: 10/3acbc2712f97de5a7b916e2e611682377b5f62efb7ebe63ede314800bedd9c63e9ae1e8b1f819efe0e87f9d813842695cfed991a276290a79cac63d48c60df05 + languageName: node + linkType: hard + +"dotenv-expand@npm:^10.0.0": + version: 10.0.0 + resolution: "dotenv-expand@npm:10.0.0" + checksum: 10/b41eb278bc96b92cbf3037ca5f3d21e8845bf165dc06b6f9a0a03d278c2bd5a01c0cfbb3528ae3a60301ba1a8a9cace30e748c54b460753bc00d4c014b675597 + languageName: node + linkType: hard + "dotenv@npm:^16.0.0, dotenv@npm:^16.4.5": version: 16.4.5 resolution: "dotenv@npm:16.4.5" @@ -7902,6 +7934,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.3.0": + version: 16.4.7 + resolution: "dotenv@npm:16.4.7" + checksum: 10/f13bfe97db88f0df4ec505eeffb8925ec51f2d56a3d0b6d916964d8b4af494e6fb1633ba5d09089b552e77ab2a25de58d70259b2c5ed45ec148221835fc99a0c + languageName: node + linkType: hard + "dset@npm:^3.1.2": version: 3.1.3 resolution: "dset@npm:3.1.3" @@ -9266,6 +9305,7 @@ __metadata: cypress: "npm:^13.13.2" cypress-plugin-tab: "npm:^1.0.5" cypress-real-events: "npm:^1.13.0" + dotenv-cli: "npm:^8.0.0" eslint: "npm:^9.8.0" eslint-plugin-cypress: "npm:^3.4.0" eslint-plugin-json: "npm:^4.0.1" @@ -11966,7 +12006,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.8": +"minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f