From bd1e5b540327f280da94a12ca66fc0853df7d8e5 Mon Sep 17 00:00:00 2001 From: Niraj Nandish Date: Wed, 8 May 2024 02:19:36 +0530 Subject: [PATCH] feat: migrate mobile learn test to playwright (#54686) Co-authored-by: Naomi --- .eslintignore | 2 +- .github/workflows/e2e-mobile.yml | 41 +++++---------- cypress/e2e/mobile-learn/test-challenges.js | 34 ------------ e2e/mobile/mobile-learn.spec.ts | 58 +++++++++++++++++++++ playwright-mobile.config.ts | 53 +++++++++++++++++++ playwright.config.ts | 2 +- 6 files changed, 127 insertions(+), 63 deletions(-) delete mode 100644 cypress/e2e/mobile-learn/test-challenges.js create mode 100644 e2e/mobile/mobile-learn.spec.ts create mode 100644 playwright-mobile.config.ts diff --git a/.eslintignore b/.eslintignore index ed9547f251d..54d4fb64c7a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,4 +9,4 @@ shared/config/donation-settings.js shared/config/superblocks.js web/** docs/**/*.md -playwright.config.ts +playwright*.config.ts diff --git a/.github/workflows/e2e-mobile.yml b/.github/workflows/e2e-mobile.yml index 71298da0b73..9c554539b81 100644 --- a/.github/workflows/e2e-mobile.yml +++ b/.github/workflows/e2e-mobile.yml @@ -29,16 +29,15 @@ jobs: uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: node-version: ${{ matrix.node-version }} - # cypress-io/github-action caches the store, so we should not cache it - # here. + cache: pnpm - - name: Setup Flutter 3.13.x + - name: Setup Flutter 3.19.x uses: subosito/flutter-action@2783a3f08e1baf891508463f8c6653c258246225 # v2 with: - flutter-version: '3.13.x' + flutter-version: '3.19.x' channel: 'stable' cache: true - cache-key: flutter-3.13.x + cache-key: flutter-3.19.x cache-path: ${{ runner.tool_cache }}/flutter - name: Set freeCodeCamp Environment Variables @@ -58,30 +57,18 @@ jobs: flutter pub get flutter test test/widget_test.dart - # This is a workaround for the fact that Cypress does not support - # running in a sub-directory. - # - # In our cypress config, we default to the cypress/e2e/default directory. - # We need to change this to cypress/e2e/ for the specific tests we are running - # in this workflow. - # - - name: Adjust the Cypress Config - run: | - sed -i 's#cypress/e2e/default/#cypress/e2e/#g' cypress.config.js + - name: Install playwright dependencies + run: npx playwright install --with-deps - name: Install serve run: npm install -g serve - - name: Cypress run - uses: cypress-io/github-action@v6 + - name: Run playwright tests + run: npx playwright test --config=playwright-mobile.config.ts + + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} with: - record: ${{ env.CYPRESS_RECORD_KEY != 0 }} - start: npx serve --no-request-logging - wait-on: http://localhost:3000 - wait-on-timeout: 1200 - config: retries=1,screenshotOnRunFailure=false,video=false,baseUrl=http://localhost:3000/mobile/mobile-app/generated-tests/ - browser: chrome - spec: cypress/e2e/mobile-learn/test-challenges.js - env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + name: playwright-report-mobile + path: playwright/reporter + retention-days: 30 diff --git a/cypress/e2e/mobile-learn/test-challenges.js b/cypress/e2e/mobile-learn/test-challenges.js deleted file mode 100644 index 3f6cf9ccbcf..00000000000 --- a/cypress/e2e/mobile-learn/test-challenges.js +++ /dev/null @@ -1,34 +0,0 @@ -import currData from '../../../shared/config/curriculum.json'; -import { orderedSuperBlockInfo } from '../../../tools/scripts/build/build-external-curricula-data'; - -const nonEditorSB = [ - 'python-for-everybody', - 'data-analysis-with-python', - 'machine-learning-with-python' -]; - -const publicSB = orderedSuperBlockInfo - .filter(sb => sb.public === true && !nonEditorSB.includes(sb.dashedName)) - .map(sb => sb.dashedName); - -describe('Test challenges in mobile', () => { - for (let superBlock of publicSB) { - for (let currBlock of Object.values(currData[superBlock]['blocks'])) { - describe(`SuperBlock: ${superBlock} - Block: ${currBlock['meta']['name']}`, () => { - for (let currChallenge of currBlock['challenges']) { - it(`Challenge: ${currChallenge['title']}(${currChallenge['id']})`, () => { - cy.visit( - `/${superBlock}/${currChallenge['block']}/${currChallenge['id']}`, - { - onBeforeLoad(win) { - cy.spy(win.console, 'log').as('consoleLog'); - } - } - ); - cy.get('@consoleLog').should('be.calledWith', 'completed'); - }); - } - }); - } - } -}); diff --git a/e2e/mobile/mobile-learn.spec.ts b/e2e/mobile/mobile-learn.spec.ts new file mode 100644 index 00000000000..b0bdef6a94c --- /dev/null +++ b/e2e/mobile/mobile-learn.spec.ts @@ -0,0 +1,58 @@ +import { expect, test } from '@playwright/test'; + +import currData from '../../shared/config/curriculum.json'; +import { orderedSuperBlockInfo } from '../../tools/scripts/build/build-external-curricula-data'; + +interface Curriculum { + [key: string]: { + blocks: { + [key: string]: { + challenges: { + id: string; + title: string; + block: string; + }[]; + meta: { + name: string; + }; + }; + }; + }; +} + +const nonEditorSB = [ + 'python-for-everybody', + 'data-analysis-with-python', + 'machine-learning-with-python' +]; + +const publicSB = orderedSuperBlockInfo + .filter(sb => sb.public === true && !nonEditorSB.includes(sb.dashedName)) + .map(sb => sb.dashedName); + +const typedCurriculum = currData as Curriculum; + +test.describe('Test challenges in mobile', () => { + for (const superBlock of publicSB) { + for (const currBlock of Object.values( + typedCurriculum[superBlock]['blocks'] + )) { + test.describe(`SuperBlock: ${superBlock} - Block: ${currBlock['meta']['name']}`, () => { + for (const currChallenge of currBlock['challenges']) { + test(`Challenge: ${currChallenge['title']}(${currChallenge['id']})`, async ({ + page + }) => { + const logMsges: string[] = []; + page.on('console', msg => { + logMsges.push(msg.text()); + }); + await page.goto( + `/${superBlock}/${currChallenge['block']}/${currChallenge['id']}` + ); + expect(logMsges).toContain('completed'); + }); + } + }); + } + } +}); diff --git a/playwright-mobile.config.ts b/playwright-mobile.config.ts new file mode 100644 index 00000000000..0de50bf076a --- /dev/null +++ b/playwright-mobile.config.ts @@ -0,0 +1,53 @@ +import path from 'path'; +import { config as dotenvConfig } from 'dotenv'; +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +const envPath = path.resolve(__dirname, '.env'); +dotenvConfig({ path: envPath }); +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: 'e2e', + testMatch: 'mobile/*.spec.ts', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [['html', { outputFolder: 'playwright/reporter' }]], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + timeout: 15 * 1000, + outputDir: 'playwright/test-results', + + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://127.0.0.1:3000/', + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + /* Use custom test attribute */ + testIdAttribute: 'data-playwright-test-label' + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'Mobile Chrome', + use: { ...devices['Pixel 5'] } + } + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'cd mobile/mobile-app && npx serve generated-tests', + url: 'http://127.0.0.1:3000', + reuseExistingServer: !process.env.CI + } +}); diff --git a/playwright.config.ts b/playwright.config.ts index 338b0483b7d..04600d4101b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -13,7 +13,7 @@ dotenvConfig({ path: envPath }); */ export default defineConfig({ testDir: 'e2e', - testMatch: '*.spec.ts', + testMatch: '!(mobile)*.spec.ts', /* Run tests in files in parallel */ fullyParallel: false, /* Fail the build on CI if you accidentally left test.only in the source code. */