feat(test): added e2e tests
This commit is contained in:
		
							parent
							
								
									ebfdb64fde
								
							
						
					
					
						commit
						f81012aee0
					
				
							
								
								
									
										23
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.github/workflows/playwright.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| name: E2E tests | ||||
| on: | ||||
|   pull_request: | ||||
|   push: | ||||
|     branches: | ||||
|       - main | ||||
| jobs: | ||||
|   test: | ||||
|     timeout-minutes: 60 | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - run: corepack enable | ||||
|       - uses: actions/setup-node@v3 | ||||
|         with: | ||||
|           node-version: 16 | ||||
|           cache: 'pnpm' | ||||
|       - name: Install dependencies | ||||
|         run: pnpm install | ||||
|       - name: Install Playwright Browsers | ||||
|         run: pnpm exec playwright install --with-deps | ||||
|       - name: Run Playwright tests | ||||
|         run: pnpm exec playwright test | ||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -27,4 +27,7 @@ coverage | ||||
| *.sln | ||||
| *.sw? | ||||
| 
 | ||||
| .env | ||||
| .env | ||||
| /test-results/ | ||||
| /playwright-report/ | ||||
| /playwright/.cache/ | ||||
|  | ||||
							
								
								
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -25,7 +25,6 @@ declare module '@vue/runtime-core' { | ||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||
|     NColorPicker: typeof import('naive-ui')['NColorPicker'] | ||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||
|     NCopyableInput: typeof import('naive-ui')['NCopyableInput'] | ||||
|     NDatePicker: typeof import('naive-ui')['NDatePicker'] | ||||
|     NDivider: typeof import('naive-ui')['NDivider'] | ||||
|     NDynamicInput: typeof import('naive-ui')['NDynamicInput'] | ||||
|  | ||||
| @ -25,6 +25,7 @@ | ||||
|     "preview": "vite preview --port 5050", | ||||
|     "test": "npm run test:unit", | ||||
|     "test:unit": "vitest --environment jsdom", | ||||
|     "test:e2e": "playwright test", | ||||
|     "coverage": "vitest run --coverage", | ||||
|     "typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", | ||||
|     "lint": "eslint src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --ignore-path .gitignore", | ||||
| @ -75,6 +76,7 @@ | ||||
|     "vue-router": "^4.1.6" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@playwright/test": "^1.32.2", | ||||
|     "@rushstack/eslint-patch": "^1.2.0", | ||||
|     "@types/bcryptjs": "^2.4.2", | ||||
|     "@types/crypto-js": "^4.1.1", | ||||
|  | ||||
							
								
								
									
										80
									
								
								playwright.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								playwright.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | ||||
| 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({ | ||||
|   testDir: './src', | ||||
|   testMatch: /.*\.e2e\.(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, | ||||
|   /* 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', | ||||
| 
 | ||||
|     testIdAttribute: 'data-test-id', | ||||
|   }, | ||||
| 
 | ||||
|   /* 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 dev', | ||||
|     url: 'http://127.0.0.1:3000', | ||||
|     reuseExistingServer: !process.env.CI, | ||||
|   }, | ||||
| }); | ||||
							
								
								
									
										20
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -126,6 +126,9 @@ dependencies: | ||||
|     version: 4.1.6(vue@3.2.47) | ||||
| 
 | ||||
| devDependencies: | ||||
|   '@playwright/test': | ||||
|     specifier: ^1.32.2 | ||||
|     version: 1.32.2 | ||||
|   '@rushstack/eslint-patch': | ||||
|     specifier: ^1.2.0 | ||||
|     version: 1.2.0 | ||||
| @ -1721,6 +1724,17 @@ packages: | ||||
|       tslib: 2.5.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /@playwright/test@1.32.2: | ||||
|     resolution: {integrity: sha512-nhaTSDpEdTTttdkDE8Z6K3icuG1DVRxrl98Qq0Lfc63SS9a2sjc9+x8ezysh7MzCKz6Y+nArml3/mmt+gqRmQQ==} | ||||
|     engines: {node: '>=14'} | ||||
|     hasBin: true | ||||
|     dependencies: | ||||
|       '@types/node': 16.18.22 | ||||
|       playwright-core: 1.32.2 | ||||
|     optionalDependencies: | ||||
|       fsevents: 2.3.2 | ||||
|     dev: true | ||||
| 
 | ||||
|   /@polka/url@1.0.0-next.21: | ||||
|     resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} | ||||
|     dev: true | ||||
| @ -6800,6 +6814,12 @@ packages: | ||||
|     engines: {node: '>=10'} | ||||
|     dev: false | ||||
| 
 | ||||
|   /playwright-core@1.32.2: | ||||
|     resolution: {integrity: sha512-zD7aonO+07kOTthsrCR3YCVnDcqSHIJpdFUtZEMOb6//1Rc7/6mZDRdw+nlzcQiQltOOsiqI3rrSyn/SlyjnJQ==} | ||||
|     engines: {node: '>=14'} | ||||
|     hasBin: true | ||||
|     dev: true | ||||
| 
 | ||||
|   /pngjs@5.0.0: | ||||
|     resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} | ||||
|     engines: {node: '>=10.13.0'} | ||||
|  | ||||
| @ -5,6 +5,7 @@ import { fileURLToPath } from 'url'; | ||||
| const currentDirname = dirname(fileURLToPath(import.meta.url)); | ||||
| 
 | ||||
| const toolsDir = join(currentDirname, '..', 'src', 'tools'); | ||||
| // eslint-disable-next-line no-undef
 | ||||
| const toolName = process.argv[2]; | ||||
| 
 | ||||
| if (!toolName) { | ||||
| @ -73,6 +74,28 @@ import { expect, describe, it } from 'vitest'; | ||||
| `,
 | ||||
| ); | ||||
| 
 | ||||
| createToolFile( | ||||
|   `${toolName}.e2e.spec.ts`, | ||||
|   ` | ||||
| import { test, expect } from '@playwright/test'; | ||||
| 
 | ||||
| test.describe('Tool - ${toolNameTitleCase}', () => { | ||||
|   test.beforeEach(async ({ page }) => { | ||||
|     await page.goto('/${toolName}'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Has correct title', async ({ page }) => { | ||||
|     await expect(page).toHaveTitle('${toolNameTitleCase} - IT Tools'); | ||||
|   }); | ||||
| 
 | ||||
|   test('', async ({ page }) => { | ||||
| 
 | ||||
|   }); | ||||
| }); | ||||
|    | ||||
| `,
 | ||||
| ); | ||||
| 
 | ||||
| const toolsIndex = join(toolsDir, 'index.ts'); | ||||
| const indexContent = await readFile(toolsIndex, { encoding: 'utf-8' }).then((r) => r.split('\n')); | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,48 @@ | ||||
| import { test, expect } from '@playwright/test'; | ||||
| 
 | ||||
| test.describe('Tool - OTP code generator', () => { | ||||
|   test.beforeEach(async ({ page }) => { | ||||
|     await page.goto('/otp-generator'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Has title', async ({ page }) => { | ||||
|     await expect(page).toHaveTitle('OTP code generator - IT Tools'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Secret hexa value is computed from provided secret', async ({ page }) => { | ||||
|     await page.getByPlaceholder('Paste your TOTP secret...').fill('ITTOOLS'); | ||||
| 
 | ||||
|     const secretInHex = await page.getByPlaceholder('Secret in hex will be displayed here').inputValue(); | ||||
| 
 | ||||
|     expect(secretInHex).toEqual('44e6e72e02'); | ||||
|   }); | ||||
| 
 | ||||
|   test('OTP a generated from the provided secret', async ({ page }) => { | ||||
|     page.evaluate(() => { | ||||
|       Date.now = () => 1609477200000; //Jan 1, 2021
 | ||||
|     }); | ||||
| 
 | ||||
|     await page.getByPlaceholder('Paste your TOTP secret...').fill('ITTOOLS'); | ||||
| 
 | ||||
|     const previousOtp = await page.getByTestId('previous-otp').innerText(); | ||||
|     const currentOtp = await page.getByTestId('current-otp').innerText(); | ||||
|     const nextOtp = await page.getByTestId('next-otp').innerText(); | ||||
| 
 | ||||
|     expect(previousOtp.trim()).toEqual('028034'); | ||||
|     expect(currentOtp.trim()).toEqual('162195'); | ||||
|     expect(nextOtp.trim()).toEqual('452815'); | ||||
|   }); | ||||
| 
 | ||||
|   test('You can generate a new random secret', async ({ page }) => { | ||||
|     const initialSecret = await page.getByPlaceholder('Paste your TOTP secret...').inputValue(); | ||||
|     await page | ||||
|       .locator('div') | ||||
|       .filter({ hasText: /^Secret$/ }) | ||||
|       .getByRole('button') | ||||
|       .click(); | ||||
| 
 | ||||
|     const newSecret = await page.getByPlaceholder('Paste your TOTP secret...').inputValue(); | ||||
| 
 | ||||
|     expect(newSecret).not.toEqual(initialSecret); | ||||
|   }); | ||||
| }); | ||||
| @ -8,13 +8,21 @@ | ||||
|     <n-input-group> | ||||
|       <n-tooltip trigger="hover" placement="bottom"> | ||||
|         <template #trigger> | ||||
|           <n-button secondary @click.prevent="copyPrevious(tokens.previous)">{{ tokens.previous }}</n-button> | ||||
|           <n-button data-test-id="previous-otp" secondary @click.prevent="copyPrevious(tokens.previous)">{{ | ||||
|             tokens.previous | ||||
|           }}</n-button> | ||||
|         </template> | ||||
|         <div>{{ previousCopied ? 'Copied !' : 'Copy previous OTP' }}</div> | ||||
|       </n-tooltip> | ||||
|       <n-tooltip trigger="hover" placement="bottom"> | ||||
|         <template #trigger> | ||||
|           <n-button tertiary type="primary" class="current-otp" @click.prevent="copyCurrent(tokens.current)"> | ||||
|           <n-button | ||||
|             tertiary | ||||
|             type="primary" | ||||
|             data-test-id="current-otp" | ||||
|             class="current-otp" | ||||
|             @click.prevent="copyCurrent(tokens.current)" | ||||
|           > | ||||
|             {{ tokens.current }} | ||||
|           </n-button> | ||||
|         </template> | ||||
| @ -22,7 +30,9 @@ | ||||
|       </n-tooltip> | ||||
|       <n-tooltip trigger="hover" placement="bottom"> | ||||
|         <template #trigger> | ||||
|           <n-button secondary @click.prevent="copyNext(tokens.next)">{{ tokens.next }}</n-button> | ||||
|           <n-button secondary data-test-id="next-otp" @click.prevent="copyNext(tokens.next)">{{ | ||||
|             tokens.next | ||||
|           }}</n-button> | ||||
|         </template> | ||||
|         <div>{{ nextCopied ? 'Copied !' : 'Copy next OTP' }}</div> | ||||
|       </n-tooltip> | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/tools/token-generator/token-generator.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/tools/token-generator/token-generator.e2e.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| import { test, expect } from '@playwright/test'; | ||||
| 
 | ||||
| test.describe('Tool - Token generator', () => { | ||||
|   test.beforeEach(async ({ page }) => { | ||||
|     await page.goto('/token-generator'); | ||||
|   }); | ||||
| 
 | ||||
|   test('Has title', async ({ page }) => { | ||||
|     await expect(page).toHaveTitle('Token generator - IT Tools'); | ||||
|   }); | ||||
| 
 | ||||
|   test('New token on refresh', async ({ page }) => { | ||||
|     const initialToken = await page.getByPlaceholder('The token...').inputValue(); | ||||
|     await page.getByRole('button', { name: 'Refresh' }).click(); | ||||
|     const newToken = await page.getByPlaceholder('The token...').inputValue(); | ||||
| 
 | ||||
|     expect(newToken).not.toEqual(initialToken); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										13
									
								
								vitest.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vitest.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| import { configDefaults, defineConfig } from 'vitest/config'; | ||||
| import path from 'path'; | ||||
| 
 | ||||
| export default defineConfig({ | ||||
|   resolve: { | ||||
|     alias: { | ||||
|       '@': path.resolve(__dirname, './src'), | ||||
|     }, | ||||
|   }, | ||||
|   test: { | ||||
|     exclude: [...configDefaults.exclude, '**/*.e2e.spec.ts'], | ||||
|   }, | ||||
| }); | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user