diff --git a/.eslintrc.js b/.eslintrc.js index 0f3c6e0..1b4bf93 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -73,7 +73,7 @@ module.exports = { 'import/prefer-default-export': 0, 'import/no-extraneous-dependencies': 0, - 'import/no-default-export': 2, + 'import/no-default-export': 0, 'import/no-self-import': 2, 'import/no-named-as-default': 2, 'import-helpers/order-imports': [ diff --git a/.github/workflows/mr-ci.yml b/.github/workflows/mr-ci.yml new file mode 100644 index 0000000..b45c719 --- /dev/null +++ b/.github/workflows/mr-ci.yml @@ -0,0 +1,26 @@ +name: 'Merge Request CI' + +on: + pull_request: + branches: [ "main" ] + +# 定义工作 job +jobs: + build: + runs-on: ubuntu-latest + name: build + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: '18.x' + - name: Installing my packages + run: npm install + - name: Lint + run: npm run lint + - name: Build App + run: npm run build + - name: Test + run: npm run test:ci diff --git a/.gitignore b/.gitignore index 4752b34..5fd9247 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,8 @@ build dist npm-debug.log .idea -*.log \ No newline at end of file +*.log +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package-lock.json b/package-lock.json index a972040..b25777e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "fe-optimization-demo", "version": "1.0.0", "license": "MIT", "dependencies": { @@ -33,6 +34,7 @@ "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", "@hot-loader/react-dom": "^17.0.2", + "@playwright/test": "^1.41.1", "@types/history": "^4.7.9", "@types/marked": "^4.0.2", "@types/node": "^17.0.21", @@ -2758,6 +2760,21 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.1.tgz", + "integrity": "sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw==", + "dev": true, + "dependencies": { + "playwright": "1.41.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -7370,6 +7387,20 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "dev": true, @@ -10562,6 +10593,36 @@ "node": ">=6" } }, + "node_modules/playwright": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz", + "integrity": "sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==", + "dev": true, + "dependencies": { + "playwright-core": "1.41.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz", + "integrity": "sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/pngjs": { "version": "3.4.0", "dev": true, @@ -16152,6 +16213,15 @@ "fastq": "^1.6.0" } }, + "@playwright/test": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.1.tgz", + "integrity": "sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw==", + "dev": true, + "requires": { + "playwright": "1.41.1" + } + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -19173,6 +19243,13 @@ "version": "1.0.0", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "dev": true @@ -21211,6 +21288,22 @@ } } }, + "playwright": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz", + "integrity": "sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.41.1" + } + }, + "playwright-core": { + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz", + "integrity": "sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==", + "dev": true + }, "pngjs": { "version": "3.4.0", "dev": true diff --git a/package.json b/package.json index f23ecda..53e4ceb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "*.css" ], "scripts": { - "prepare": "husky install", + "prepare": "husky install && npx playwright install chromium", "commit": "cross-env git-cz", "dev": "cross-env NODE_ENV=development webpack serve", "build": "cross-env NODE_ENV=production webpack", @@ -15,6 +15,7 @@ "clean": "rm dist", "lint:file": "cross-env eslint . --ext .js,.jsx,.ts,.tsx --fix", "lint": "cross-env eslint . --ext .js,.jsx,.ts,.tsx --fix.", + "test:ci": "npx playwright test", "pretty": "cross-env prettier --write '**/*.{j,t}{s,sx}'", "typecheck": "cross-env tsc" }, @@ -39,6 +40,7 @@ "@commitlint/cli": "^16.2.1", "@commitlint/config-conventional": "^16.2.1", "@hot-loader/react-dom": "^17.0.2", + "@playwright/test": "^1.41.1", "@types/history": "^4.7.9", "@types/marked": "^4.0.2", "@types/node": "^17.0.21", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..32f821b --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,85 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + webServer: { + command: 'npm run dev', + port: 4100, + reuseExistingServer: !process.env.CI, + stdout: 'pipe', + stderr: 'pipe', + timeout: 60 * 1000, + }, + testDir: './tests', + /* 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: { + /* 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', + }, + + /* 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: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests/app.spec.ts b/tests/app.spec.ts new file mode 100644 index 0000000..3588026 --- /dev/null +++ b/tests/app.spec.ts @@ -0,0 +1,20 @@ +// ./tests/homepage.spec.ts +import { test, expect } from '@playwright/test'; + +test('Home page should render successfully', async ({ page }) => { + await page.goto('http://localhost:4100/home/'); + + await expect(page).toHaveTitle(/《现代前端工程体验优化》示例项目/); + + await expect( + await page.getByRole('link', { name: '《现代前端工程体验优化》demo' }), + ).toBeVisible(); + const signUpPageLinkBtn = await page.getByRole('link', { name: 'Sign Up' }); + await expect(signUpPageLinkBtn).toBeVisible(); + await signUpPageLinkBtn.click(); + + await page.waitForTimeout(500); + await expect( + await page.getByRole('button', { name: 'Sign Up' }), + ).toBeVisible(); +});