Browse Source

test(e2e): add login success flow with Cypress and data-testid attributes

- Install Cypress and related configuration
- Add data-testid attributes to components for reliable element selection
- Implement e2e test case covering successful user login
dengdx 4 days ago
parent
commit
65912a40af

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+cypress/

+ 4 - 1
.gitignore

@@ -7,4 +7,7 @@ node_modules/
 .swc
 *.local
 # Cordova
-.build/dros/
+.build/dros/
+# cypress
+/cypress/videos
+/cypress/screenshots

+ 1 - 4
.lintstagedrc.json

@@ -1,7 +1,4 @@
 {
-  "*.js": "eslint --fix",
-  "*.ts": "eslint --fix",
-  "*.tsx": "eslint --fix",
-  "*.jsx": "eslint --fix",
+  "!(cypress|__e2e_test__)/**/*.{js,ts,tsx,jsx}": "eslint --fix",
   "*.{json,md}": "prettier --write"
 }

+ 23 - 0
__e2e_test__/login.spec.ts

@@ -0,0 +1,23 @@
+describe('Login Page', () => {
+  it('should successfully log in with correct credentials', () => {
+    // Visit the base URL
+    cy.visit('/');
+
+    // Wait for the page to load and ensure the elements are present
+    cy.get('[data-testid="login-username-input"]').should('be.visible');
+    cy.get('[data-testid="login-password-input"]').should('be.visible');
+    cy.get('[data-testid="login-submit-button"]').should('be.visible');
+
+    // Enter the username
+    cy.get('[data-testid="login-username-input"]').type('admin');
+
+    // Enter the password
+    cy.get('[data-testid="login-password-input"]').type('123456');
+
+    // Click the login button
+    cy.get('[data-testid="login-submit-button"]').click();
+
+    // Wait for the login process to complete
+    cy.contains('登录成功').should('be.visible', { timeout: 10000 });
+  });
+});

+ 11 - 0
cypress.config.ts

@@ -0,0 +1,11 @@
+import { defineConfig } from 'cypress';
+
+export default defineConfig({
+  e2e: {
+    baseUrl: 'http://localhost:10086/#/pages/index/index', // Adjust this to match your application's base URL
+    specPattern: '__e2e_test__/**/*.spec.ts',
+    setupNodeEvents(on, config) {
+      return require('./cypress/plugins/index.js')(on, config);
+    },
+  },
+});

+ 14 - 0
cypress/plugins/index.js

@@ -0,0 +1,14 @@
+const webpackPreprocessor = require('@cypress/webpack-preprocessor');
+
+module.exports = (on, config) => {
+  const options = {
+    webpackOptions: {
+      output: {
+        chunkFormat: 'commonjs',
+      },
+      target: 'node',
+    },
+  };
+
+  on('file:preprocessor', webpackPreprocessor(options));
+};

+ 27 - 0
cypress/support/commands.js

@@ -0,0 +1,27 @@
+// cypress/support/commands.js
+
+// ***********************************************
+// 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) => { ... })

+ 7 - 0
cypress/support/e2e.ts

@@ -0,0 +1,7 @@
+// cypress/support/e2e.ts
+
+// Import commands.js using ES2015 syntax:
+import './commands';
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')

File diff suppressed because it is too large
+ 269 - 68
package-lock.json


+ 4 - 1
package.json

@@ -37,7 +37,7 @@
     "prebuild:arm:linux": "cp ./.build/taro.linux-arm64-gnu.node  ./node_modules/@tarojs/binding",
     "build:electron:win": "electron-builder --config electron-builder.json  --win",
     "build:electron:linux": "electron-builder --config electron-builder.json  --linux",
-    "build:android":"node ./.build/build-android.js"
+    "build:android": "node ./.build/build-android.js"
   },
   "browserslist": [
     "defaults and fully supports es6-module",
@@ -86,10 +86,12 @@
     "@babel/core": "^7.24.4",
     "@babel/plugin-transform-class-properties": "7.25.9",
     "@babel/preset-react": "^7.24.1",
+    "@cypress/webpack-preprocessor": "^7.0.0",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
     "@tarojs/cli": "4.1.1",
     "@tarojs/taro-loader": "4.1.1",
     "@tarojs/webpack5-runner": "4.1.1",
+    "@types/cypress": "^1.1.6",
     "@types/node": "^18",
     "@types/react": "^18.0.0",
     "@types/webpack": "^5.28.5",
@@ -100,6 +102,7 @@
     "babel-preset-taro": "4.1.1",
     "browserify-fs": "^1.0.0",
     "cordova": "^12.0.0",
+    "cypress": "^14.5.4",
     "electron-builder": "^26.0.12",
     "eslint": "^9.28.0",
     "eslint-config-prettier": "^10.1.5",

+ 12 - 3
src/pages/security/Login.tsx

@@ -88,15 +88,23 @@ const Login: React.FC = () => {
               name="username"
               rules={[{ required: true, message: '请输入用户名' }]}
             >
-              <Input size="large" placeholder="请输入用户名" />
+              <Input
+                size="large"
+                placeholder="请输入用户名"
+                data-testid="login-username-input"
+              />
             </Form.Item>
             <Form.Item
               label="密码"
               name="password"
               rules={[{ required: true, message: '请输入密码' }]}
-              
             >
-              <Input.Password size="large" placeholder="请输入密码" className="py-0 px-[11px]" />
+              <Input.Password
+                size="large"
+                placeholder="请输入密码"
+                className="py-0 px-[11px]"
+                data-testid="login-password-input"
+              />
             </Form.Item>
             <Form.Item>
               <Row gutter={16}>
@@ -106,6 +114,7 @@ const Login: React.FC = () => {
                     htmlType="submit"
                     size="large"
                     className="w-full"
+                    data-testid="login-submit-button"
                   >
                     登录
                   </Button>

Some files were not shown because too many files changed in this diff