diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..240ad5d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,27 @@ +name: E2E Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - name: Install dependencies + run: yarn + - name: Install Playwright Browsers + run: yarn playwright install --with-deps + - name: Run Playwright tests + run: yarn playwright test + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/e2e/.gitignore b/e2e/.gitignore new file mode 100644 index 0000000..75e854d --- /dev/null +++ b/e2e/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 0000000..8a9b0cf --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,10 @@ +{ + "name": "test", + "version": "1.0.0", + "main": "index.js", + "license": "Apache-2.0", + "devDependencies": { + "@playwright/test": "^1.30.0" + }, + "scripts": {} +} diff --git a/e2e/playwright.config.js b/e2e/playwright.config.js new file mode 100644 index 0000000..6fbfe26 --- /dev/null +++ b/e2e/playwright.config.js @@ -0,0 +1,92 @@ +// @ts-check +const { defineConfig, devices } = require('@playwright/test'); + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * @see https://playwright.dev/docs/test-configuration + */ +module.exports = defineConfig({ + testDir: './tests', + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000 + }, + /* 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, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { channel: 'chrome' }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}); + diff --git a/e2e/tests/all.spec.js b/e2e/tests/all.spec.js new file mode 100644 index 0000000..443e1ed --- /dev/null +++ b/e2e/tests/all.spec.js @@ -0,0 +1,37 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); + +test('e2e test', async ({ page }) => { + await page.goto('https://bytedream.marlene.top/'); + await page.locator('.to_top_btn').first().click(); // 点击置顶组件 + await page.getByText('完整榜单').click(); + await page.getByRole('button', { name: '去签到' }).click(); + await page.locator('.alter-items > div:nth-child(2)').first().click(); + await page.locator('.hover\\:\\!text-jj-blue-normal').click(); + await page.locator('.type-manage > .alter-prev').click(); + await page.locator('#__nuxt > main > div.root > div > div > div.view-types.left-0 > div > div:nth-child(2) > a').click(); // 切换标签 + await page.getByRole('link', { name: '热榜' }).click(); + await page.locator('\/\/*[@id="__nuxt"]/main/div[3]/div/div/div[2]/div[1]/div[1]/div[2]/div').click(); // 切换选项 + await page.getByRole('link', { name: '30天内' }).click(); + await page.getByRole('button', { name: 'Toggle theme' }).click(); + await page.locator('.ad_close').first().click(); + await page.locator('.ad_close').first().click(); + await page.getByRole('link', { name: 'Back to home' }).click(); + const page1Promise = page.waitForEvent('popup'); + await page.locator('\/\/*[@id="__nuxt"]/main/div[3]/div/div/div[2]/div[1]/ul/li[5]/a').click(); // 选择某篇文章 + const page1 = await page1Promise; + await page1.locator('.panel-btn').first().click(); + const page2Promise = page1.waitForEvent('popup'); + await page1.locator('\/\/*[@id="__nuxt"]/main/div[3]/main/div/div[3]/div[3]/nav/div[2]/a').click(); // 专栏文章切换 + const page2 = await page2Promise; + const page3Promise = page2.waitForEvent('popup'); + await page2.locator('\/\/*[@id="__nuxt"]/main/div[3]/main/div/div[3]/div[2]/div/div[2]/div/a[1]').click(); // 选择相关文章 + const page3 = await page3Promise; + await page3.getByPlaceholder('搜索').click(); + await page3.getByPlaceholder('搜索').press('CapsLock'); + await page3.getByPlaceholder('搜索').fill('MonoRepo'); + await page3.locator('.search-icon').click(); + await page3.locator('\/\/*[@id="__nuxt"]/main/div[1]/header/div/div/div/div[2]/div[2]/a[1]').click(); // 选择搜索结果 + await page3.getByRole('button', { name: 'Toggle theme' }).click(); + await page3.getByRole('link', { name: '首页' }).click(); +}); \ No newline at end of file diff --git a/package.json b/package.json index ed65078..d9e785e 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,14 @@ "build": "npx turbo build --no-cache --force", "lint:fix": "npx turbo lint:fix", "typecheck": "npx turbo typecheck", - "start": "npx turbo start" + "start": "npx turbo start", + "test": "npx turbo test" }, "packageManager": "yarn@1.22.19", "workspaces": [ "frontend", - "backend" + "backend", + "e2e" ], "devDependencies": { "husky": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 122886b..9b85831 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2879,6 +2879,14 @@ resolved "https://registry.npmmirror.com/@planetscale/database/-/database-1.5.0.tgz#073d9ca9841ad62896a6e31f610e89112e6264ef" integrity sha512-Qwh7Or1W5dB5mZ9EQqDkgvkDKhBBmQe58KIVUy0SGocNtr5fP4JAWtvZ6EdLAV6C6hVpzNlCA2xIg9lKTswm1Q== +"@playwright/test@^1.30.0": + version "1.30.0" + resolved "https://registry.npmmirror.com/@playwright/test/-/test-1.30.0.tgz#8c0c4930ff2c7be7b3ec3fd434b2a3b4465ed7cb" + integrity sha512-SVxkQw1xvn/Wk/EvBnqWIq6NLo1AppwbYOjNLmyU0R1RoQ3rLEBtmjTnElcnz8VEtn11fptj1ECxK0tgURhajw== + dependencies: + "@types/node" "*" + playwright-core "1.30.0" + "@pmmmwh/react-refresh-webpack-plugin@0.5.10": version "0.5.10" resolved "https://registry.npmmirror.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" @@ -14956,7 +14964,7 @@ pkg-types@^1.0.2: mlly "^1.1.1" pathe "^1.1.0" -playwright-core@^1.30.0: +playwright-core@1.30.0, playwright-core@^1.30.0: version "1.30.0" resolved "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.30.0.tgz#de987cea2e86669e3b85732d230c277771873285" integrity sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g==