init 003_test,
This commit is contained in:
9
003_test/.editorconfig
Normal file
9
003_test/.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
58
003_test/.gitignore
vendored
Normal file
58
003_test/.gitignore
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
**/*del
|
||||
**/*bak
|
||||
**/*log
|
||||
**/*tmp
|
||||
|
||||
.env
|
||||
.env.production
|
||||
|
||||
**/*.draft
|
||||
**/~*
|
||||
**/*copy*.tsx
|
||||
**/*copy.tsx
|
||||
|
||||
# **/repomix-output.xml
|
||||
**/*:Zone.Identifier
|
||||
**/*.bak
|
||||
**/*.log
|
||||
**/*.tmp
|
||||
**/*.del
|
||||
**/*.plan
|
||||
**/_archive
|
||||
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
.swc
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
6
003_test/.vscode/extensions.json
vendored
Normal file
6
003_test/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"foxundermoon.shell-format",
|
||||
"redhat.vscode-yaml"
|
||||
]
|
||||
}
|
8
003_test/.vscode/settings.json
vendored
Normal file
8
003_test/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"[dockerfile]": {
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true
|
||||
},
|
||||
"editor.defaultFormatter": "foxundermoon.shell-format"
|
||||
}
|
||||
}
|
9
003_test/001_desktop/01_test_seat/app/.editorconfig
Normal file
9
003_test/001_desktop/01_test_seat/app/.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
7
003_test/001_desktop/01_test_seat/app/.gitignore
vendored
Normal file
7
003_test/001_desktop/01_test_seat/app/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
# Playwright
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
10
003_test/001_desktop/01_test_seat/app/.prettierrc
Normal file
10
003_test/001_desktop/01_test_seat/app/.prettierrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"printWidth": 120,
|
||||
"quoteProps": "consistent",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"plugins": []
|
||||
}
|
89
003_test/001_desktop/01_test_seat/app/package-lock.json
generated
Normal file
89
003_test/001_desktop/01_test_seat/app/package-lock.json
generated
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"prettier": "^3.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@types/node": "^22.15.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.52.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.15.18",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.52.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
17
003_test/001_desktop/01_test_seat/app/package.json
Normal file
17
003_test/001_desktop/01_test_seat/app/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@types/node": "^22.15.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": "^3.5.3"
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('has title', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Playwright/);
|
||||
});
|
||||
|
||||
test('get started link', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
|
||||
// Click the get started link.
|
||||
await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// Expects page to have a heading with the name of Installation.
|
||||
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
});
|
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { HELLO } from '../_config/helloworld';
|
||||
|
||||
test('fresh user should appears sign in page', async ({ page }) => {
|
||||
await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
console.log({ HELLO });
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Sign in | Custom | Auth | demo cms/);
|
||||
});
|
||||
|
||||
// test('fresh user login', async ({ page }) => {
|
||||
// await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
// // Expect a title "to contain" a substring.
|
||||
// const emailField = page.getByPlaceholder('e.g. admin@123.com');
|
||||
|
||||
// await emailField.press('Enter');
|
||||
// });
|
||||
|
||||
// test('get started link', async ({ page }) => {
|
||||
// await page.goto('https://playwright.dev/');
|
||||
|
||||
// // Click the get started link.
|
||||
// await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// // Expects page to have a heading with the name of Installation.
|
||||
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
// });
|
77
003_test/001_desktop/01_test_seat/app/playwright.config.ts
Normal file
77
003_test/001_desktop/01_test_seat/app/playwright.config.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// import dotenv from 'dotenv';
|
||||
// import path from 'path';
|
||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
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,
|
||||
// },
|
||||
});
|
12
003_test/001_desktop/01_test_seat/app/run.sh
Executable file
12
003_test/001_desktop/01_test_seat/app/run.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# npx playwright test
|
||||
npx playwright --version
|
||||
|
||||
npx playwright test --ui
|
||||
|
||||
npx playwright show-report --host 0.0.0.0
|
||||
|
||||
echo "done"
|
7
003_test/001_desktop/01_test_seat/app/scripts/001_setup.sh
Executable file
7
003_test/001_desktop/01_test_seat/app/scripts/001_setup.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
|
||||
|
||||
echo "done"
|
8
003_test/001_desktop/01_test_seat/app/scripts/002_setup_playwright.sh
Executable file
8
003_test/001_desktop/01_test_seat/app/scripts/002_setup_playwright.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
npx playwright install
|
||||
npx playwright install-deps
|
||||
|
||||
echo "done"
|
13
003_test/001_desktop/01_test_seat/app/scripts/003_google_chrome.sh
Executable file
13
003_test/001_desktop/01_test_seat/app/scripts/003_google_chrome.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "**** install chrome ****"
|
||||
apt update
|
||||
apt install -y wget
|
||||
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list
|
||||
apt update
|
||||
apt install -y google-chrome-stable
|
||||
|
||||
echo "install chrome done"
|
12
003_test/001_desktop/01_test_seat/app/scripts/run_all.sh
Executable file
12
003_test/001_desktop/01_test_seat/app/scripts/run_all.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
cd scripts
|
||||
./001_setup.sh 2>&1 | tee setup.log
|
||||
./002_setup_playwright.sh 2>&1 | tee setup.log
|
||||
sudo ./003_google_chrome.sh 2>&1 | tee setup.log
|
||||
|
||||
cd ..
|
||||
|
||||
echo "done"
|
@@ -0,0 +1,416 @@
|
||||
import { test, expect, type Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('https://demo.playwright.dev/todomvc');
|
||||
});
|
||||
|
||||
const TODO_ITEMS = ['buy some cheese', 'feed the cat', 'book a doctors appointment'] as const;
|
||||
|
||||
test.describe('New Todo', () => {
|
||||
test('should allow me to add todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create 1st todo.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list only has one todo item.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([TODO_ITEMS[0]]);
|
||||
|
||||
// Create 2nd todo.
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list now has two todo items.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
|
||||
test('should clear text input field when an item is added', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create one todo item.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Check that input is empty.
|
||||
await expect(newTodo).toBeEmpty();
|
||||
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||
});
|
||||
|
||||
test('should append new items to the bottom of the list', async ({ page }) => {
|
||||
// Create 3 items.
|
||||
await createDefaultTodos(page);
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count');
|
||||
|
||||
// Check test using different methods.
|
||||
await expect(page.getByText('3 items left')).toBeVisible();
|
||||
await expect(todoCount).toHaveText('3 items left');
|
||||
await expect(todoCount).toContainText('3');
|
||||
await expect(todoCount).toHaveText(/3/);
|
||||
|
||||
// Check all items in one call.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Mark all as completed', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to mark all items as completed', async ({ page }) => {
|
||||
// Complete all todos.
|
||||
await page.getByLabel('Mark all as complete').check();
|
||||
|
||||
// Ensure all todos have 'completed' class.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to clear the complete state of all items', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
// Check and then immediately uncheck.
|
||||
await toggleAll.check();
|
||||
await toggleAll.uncheck();
|
||||
|
||||
// Should be no completed classes.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
|
||||
});
|
||||
|
||||
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
await toggleAll.check();
|
||||
await expect(toggleAll).toBeChecked();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Uncheck first todo.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').uncheck();
|
||||
|
||||
// Reuse toggleAll locator and make sure its not checked.
|
||||
await expect(toggleAll).not.toBeChecked();
|
||||
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Assert the toggle all is checked again.
|
||||
await expect(toggleAll).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Item', () => {
|
||||
test('should allow me to mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
// Check first item.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
|
||||
// Check second item.
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await secondTodo.getByRole('checkbox').check();
|
||||
|
||||
// Assert completed class.
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).toHaveClass('completed');
|
||||
});
|
||||
|
||||
test('should allow me to un-mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
const firstTodoCheckbox = firstTodo.getByRole('checkbox');
|
||||
|
||||
await firstTodoCheckbox.check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await firstTodoCheckbox.uncheck();
|
||||
await expect(firstTodo).not.toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 0);
|
||||
});
|
||||
|
||||
test('should allow me to edit an item', async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const secondTodo = todoItems.nth(1);
|
||||
await secondTodo.dblclick();
|
||||
await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
// Explicitly assert the new text value.
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], 'buy some sausages', TODO_ITEMS[2]]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Editing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should hide other controls when editing', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item').nth(1);
|
||||
await todoItem.dblclick();
|
||||
await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
|
||||
await expect(
|
||||
todoItem.locator('label', {
|
||||
hasText: TODO_ITEMS[1],
|
||||
})
|
||||
).not.toBeVisible();
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should save edits on blur', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
|
||||
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], 'buy some sausages', TODO_ITEMS[2]]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should trim entered text', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], 'buy some sausages', TODO_ITEMS[2]]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should remove the item if an empty text string was entered', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should cancel edits on escape', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
|
||||
await expect(todoItems).toHaveText(TODO_ITEMS);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Counter', () => {
|
||||
test('should display the current number of todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count');
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
await expect(todoCount).toContainText('1');
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
await expect(todoCount).toContainText('2');
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Clear completed button', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
});
|
||||
|
||||
test('should display the correct text', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should remove completed items when clicked', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).getByRole('checkbox').check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(todoItems).toHaveCount(2);
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should be hidden when there are no items that are completed', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Persistence', () => {
|
||||
test('should persist its data', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
|
||||
await firstTodoCheck.check();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
|
||||
// Ensure there is 1 completed item.
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// Now reload.
|
||||
await page.reload();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Routing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
// make sure the app had a chance to save updated todos in storage
|
||||
// before navigating to a new view, otherwise the items can get lost :(
|
||||
// in some frameworks like Durandal
|
||||
await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
|
||||
});
|
||||
|
||||
test('should allow me to display active items', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should respect the back button', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await test.step('Showing all items', async () => {
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
await test.step('Showing active items', async () => {
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
});
|
||||
|
||||
await test.step('Showing completed items', async () => {
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
});
|
||||
|
||||
await expect(todoItem).toHaveCount(1);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should allow me to display completed items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('should allow me to display all items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should highlight the currently applied filter', async ({ page }) => {
|
||||
await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
|
||||
|
||||
//create locators for active and completed links
|
||||
const activeLink = page.getByRole('link', { name: 'Active' });
|
||||
const completedLink = page.getByRole('link', { name: 'Completed' });
|
||||
await activeLink.click();
|
||||
|
||||
// Page change - active items.
|
||||
await expect(activeLink).toHaveClass('selected');
|
||||
await completedLink.click();
|
||||
|
||||
// Page change - completed items.
|
||||
await expect(completedLink).toHaveClass('selected');
|
||||
});
|
||||
});
|
||||
|
||||
async function createDefaultTodos(page: Page) {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction((e) => {
|
||||
return JSON.parse(localStorage['react-todos']).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction((e) => {
|
||||
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkTodosInLocalStorage(page: Page, title: string) {
|
||||
return await page.waitForFunction((t) => {
|
||||
return JSON.parse(localStorage['react-todos'])
|
||||
.map((todo: any) => todo.title)
|
||||
.includes(t);
|
||||
}, title);
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
// tests/REQ0006/001_fresh-user-should-appears-sign-in-page.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
//
|
||||
import { CMS_HOST, HELLO } from '../_config/helloworld';
|
||||
import { TS_HELLO } from '../_test_set/helloworld';
|
||||
|
||||
test('fresh user should appears sign in page', async ({ page }) => {
|
||||
console.log({ HELLO, TS_HELLO });
|
||||
await page.goto(`${CMS_HOST}/dashboard`);
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Sign in | Custom | Auth | demo cms/);
|
||||
});
|
||||
|
||||
// test('fresh user login', async ({ page }) => {
|
||||
// await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
// // Expect a title "to contain" a substring.
|
||||
// const emailField = page.getByPlaceholder('e.g. admin@123.com');
|
||||
|
||||
// await emailField.press('Enter');
|
||||
// });
|
||||
|
||||
// test('get started link', async ({ page }) => {
|
||||
// await page.goto('https://playwright.dev/');
|
||||
|
||||
// // Click the get started link.
|
||||
// await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// // Expects page to have a heading with the name of Installation.
|
||||
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
// });
|
@@ -0,0 +1,40 @@
|
||||
// tests/REQ0006/001_fresh-user-should-appears-sign-in-page.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
//
|
||||
import { CMS_HOST, HELLO } from '../_config/helloworld';
|
||||
import { TS_HELLO } from '../_test_set/helloworld';
|
||||
|
||||
test('user login', async ({ page }) => {
|
||||
console.log({ HELLO, TS_HELLO });
|
||||
|
||||
await page.goto(`${CMS_HOST}/dashboard`);
|
||||
await expect(page).toHaveTitle(/Sign in | Custom | Auth | demo cms/);
|
||||
|
||||
await page.getByPlaceholder('name@example.com').pressSequentially('user5@123.com');
|
||||
await page.getByPlaceholder('password').pressSequentially('user5@123.com');
|
||||
await page.waitForTimeout(1 * 1000);
|
||||
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
await page.waitForTimeout(1 * 1000);
|
||||
|
||||
await expect(page).toHaveTitle(/192.168.222.199:3000\/dashboard/);
|
||||
});
|
||||
|
||||
// test('fresh user login', async ({ page }) => {
|
||||
// await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
// // Expect a title "to contain" a substring.
|
||||
// const emailField = page.getByPlaceholder('e.g. admin@123.com');
|
||||
|
||||
// await emailField.press('Enter');
|
||||
// });
|
||||
|
||||
// test('get started link', async ({ page }) => {
|
||||
// await page.goto('https://playwright.dev/');
|
||||
|
||||
// // Click the get started link.
|
||||
// await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// // Expects page to have a heading with the name of Installation.
|
||||
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
// });
|
@@ -0,0 +1,2 @@
|
||||
export const HELLO = 'WORLD';
|
||||
export const CMS_HOST = 'http://192.168.222.199:3000';
|
@@ -0,0 +1 @@
|
||||
export const TS_HELLO = 'WORLD';
|
11
003_test/001_desktop/01_test_seat/dc_up.sh
Executable file
11
003_test/001_desktop/01_test_seat/dc_up.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# docker compose build | tee dc_build.log
|
||||
|
||||
# docker compose kill
|
||||
# docker compose down
|
||||
docker compose up -d
|
||||
|
||||
echo "done"
|
32
003_test/001_desktop/01_test_seat/docker-compose.yml
Normal file
32
003_test/001_desktop/01_test_seat/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
name: 001_desktop
|
||||
|
||||
services:
|
||||
chromium:
|
||||
build: .
|
||||
security_opt:
|
||||
- seccomp:unconfined #optional
|
||||
environment:
|
||||
- TITLE=desktop-test-seat-1
|
||||
- FM_HOME=/app
|
||||
- PUID=1000 # default 911
|
||||
- PGID=1000 # default 911
|
||||
- TZ=Etc/Asia_HongKong
|
||||
- CHROME_CLI=https://www.gmail.com/ #optional
|
||||
# - CUSTOM_PORT=3000 # Internal port the container listens on for http if it needs to be swapped from the default 3000.
|
||||
- CUSTOM_HTTPS_PORT=3001 # Internal port the container listens on for https if it needs to be swapped from the default 3001.
|
||||
- NO_FULL=1 # Do not autmatically fullscreen applications when using openbox.
|
||||
- LC_ALL=en_US.UTF-8 # Set the locale.
|
||||
- INSTALL_PACKAGES=fonts-noto-cjk
|
||||
ports:
|
||||
# - 6000:3000
|
||||
- 6001:3001 # https vnc ip
|
||||
- 9323:9323 # report ip
|
||||
volumes:
|
||||
- ./app:/app
|
||||
shm_size: '1gb'
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: 1
|
||||
reservations:
|
||||
cpus: 0.01
|
27
003_test/001_desktop/01_test_seat/dockerfile
Normal file
27
003_test/001_desktop/01_test_seat/dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
FROM ghcr.io/linuxserver/baseimage-kasmvnc:ubuntujammy
|
||||
|
||||
# set version label
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
LABEL build_version="Linuxserver.io version:- ${VERSION} Build-date:- ${BUILD_DATE}"
|
||||
LABEL maintainer="thelamer"
|
||||
ARG DEBIAN_FRONTEND="noninteractive"
|
||||
|
||||
# title
|
||||
ENV TITLE=test-seat
|
||||
|
||||
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -qqy wget git curl
|
||||
|
||||
# Set up the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# ports and volumes
|
||||
EXPOSE 3000
|
||||
|
||||
VOLUME /config
|
||||
|
||||
COPY ./setup/ /setup
|
||||
RUN chmod +x /setup/*.sh
|
22
003_test/001_desktop/01_test_seat/setup/001_nvm.sh
Normal file
22
003_test/001_desktop/01_test_seat/setup/001_nvm.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "**** install nvm ****"
|
||||
|
||||
# Download and install Node.js (you may need to restart the terminal)
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
|
||||
|
||||
source ~/.nvm/nvm.sh
|
||||
|
||||
nvm install 20
|
||||
|
||||
nvm alias default 20
|
||||
|
||||
nvm use default
|
||||
|
||||
npm install -g yarn
|
||||
|
||||
echo "source ~/.nvm/nvm.sh" >>~/.bashrc
|
||||
|
||||
echo "install nvm done"
|
15
003_test/001_desktop/01_test_seat/setup/run_all.sh
Normal file
15
003_test/001_desktop/01_test_seat/setup/run_all.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# ls -1 /setup/0*.sh | while read script; do
|
||||
# echo "Running $script"
|
||||
# bash "$script" &
|
||||
# done
|
||||
|
||||
/setup/001_nvm.sh
|
||||
sudo /setup/002_google_chrome.sh
|
||||
|
||||
wait
|
||||
|
||||
echo "done"
|
7
003_test/001_desktop/_GUIDELINES.md
Normal file
7
003_test/001_desktop/_GUIDELINES.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# GUIDELINES
|
||||
|
||||
this folder contains test and validation of the project
|
||||
|
||||
## highlight
|
||||
|
||||
- `01_test_seat` testing for desktop
|
9
003_test/002_mobile/01_test_seat/app/.editorconfig
Normal file
9
003_test/002_mobile/01_test_seat/app/.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
7
003_test/002_mobile/01_test_seat/app/.gitignore
vendored
Normal file
7
003_test/002_mobile/01_test_seat/app/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
# Playwright
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
10
003_test/002_mobile/01_test_seat/app/.prettierrc
Normal file
10
003_test/002_mobile/01_test_seat/app/.prettierrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"printWidth": 120,
|
||||
"quoteProps": "consistent",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"plugins": []
|
||||
}
|
89
003_test/002_mobile/01_test_seat/app/package-lock.json
generated
Normal file
89
003_test/002_mobile/01_test_seat/app/package-lock.json
generated
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"prettier": "^3.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@types/node": "^22.15.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.52.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.15.18",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.52.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.52.0",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
17
003_test/002_mobile/01_test_seat/app/package.json
Normal file
17
003_test/002_mobile/01_test_seat/app/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@types/node": "^22.15.18"
|
||||
},
|
||||
"dependencies": {
|
||||
"prettier": "^3.5.3"
|
||||
}
|
||||
}
|
18
003_test/002_mobile/01_test_seat/app/parking/example.spec.ts
Normal file
18
003_test/002_mobile/01_test_seat/app/parking/example.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('has title', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Playwright/);
|
||||
});
|
||||
|
||||
test('get started link', async ({ page }) => {
|
||||
await page.goto('https://playwright.dev/');
|
||||
|
||||
// Click the get started link.
|
||||
await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// Expects page to have a heading with the name of Installation.
|
||||
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
});
|
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { HELLO } from '../_config/helloworld';
|
||||
|
||||
test('fresh user should appears sign in page', async ({ page }) => {
|
||||
await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
console.log({ HELLO });
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Sign in | Custom | Auth | demo cms/);
|
||||
});
|
||||
|
||||
// test('fresh user login', async ({ page }) => {
|
||||
// await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
// // Expect a title "to contain" a substring.
|
||||
// const emailField = page.getByPlaceholder('e.g. admin@123.com');
|
||||
|
||||
// await emailField.press('Enter');
|
||||
// });
|
||||
|
||||
// test('get started link', async ({ page }) => {
|
||||
// await page.goto('https://playwright.dev/');
|
||||
|
||||
// // Click the get started link.
|
||||
// await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// // Expects page to have a heading with the name of Installation.
|
||||
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
// });
|
77
003_test/002_mobile/01_test_seat/app/playwright.config.ts
Normal file
77
003_test/002_mobile/01_test_seat/app/playwright.config.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// import dotenv from 'dotenv';
|
||||
// import path from 'path';
|
||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
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,
|
||||
// },
|
||||
});
|
12
003_test/002_mobile/01_test_seat/app/run.sh
Executable file
12
003_test/002_mobile/01_test_seat/app/run.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# npx playwright test
|
||||
npx playwright --version
|
||||
|
||||
npx playwright test --ui
|
||||
|
||||
npx playwright show-report --host 0.0.0.0
|
||||
|
||||
echo "done"
|
7
003_test/002_mobile/01_test_seat/app/scripts/001_setup.sh
Executable file
7
003_test/002_mobile/01_test_seat/app/scripts/001_setup.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
|
||||
|
||||
echo "done"
|
8
003_test/002_mobile/01_test_seat/app/scripts/002_setup_playwright.sh
Executable file
8
003_test/002_mobile/01_test_seat/app/scripts/002_setup_playwright.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
npx playwright install
|
||||
npx playwright install-deps
|
||||
|
||||
echo "done"
|
13
003_test/002_mobile/01_test_seat/app/scripts/003_google_chrome.sh
Executable file
13
003_test/002_mobile/01_test_seat/app/scripts/003_google_chrome.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "**** install chrome ****"
|
||||
apt update
|
||||
apt install -y wget
|
||||
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
|
||||
echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list
|
||||
apt update
|
||||
apt install -y google-chrome-stable
|
||||
|
||||
echo "install chrome done"
|
12
003_test/002_mobile/01_test_seat/app/scripts/run_all.sh
Executable file
12
003_test/002_mobile/01_test_seat/app/scripts/run_all.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
cd scripts
|
||||
./001_setup.sh 2>&1 | tee setup.log
|
||||
./002_setup_playwright.sh 2>&1 | tee setup.log
|
||||
sudo ./003_google_chrome.sh 2>&1 | tee setup.log
|
||||
|
||||
cd ..
|
||||
|
||||
echo "done"
|
@@ -0,0 +1,416 @@
|
||||
import { test, expect, type Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('https://demo.playwright.dev/todomvc');
|
||||
});
|
||||
|
||||
const TODO_ITEMS = ['buy some cheese', 'feed the cat', 'book a doctors appointment'] as const;
|
||||
|
||||
test.describe('New Todo', () => {
|
||||
test('should allow me to add todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create 1st todo.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list only has one todo item.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([TODO_ITEMS[0]]);
|
||||
|
||||
// Create 2nd todo.
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list now has two todo items.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
|
||||
test('should clear text input field when an item is added', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create one todo item.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Check that input is empty.
|
||||
await expect(newTodo).toBeEmpty();
|
||||
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||
});
|
||||
|
||||
test('should append new items to the bottom of the list', async ({ page }) => {
|
||||
// Create 3 items.
|
||||
await createDefaultTodos(page);
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count');
|
||||
|
||||
// Check test using different methods.
|
||||
await expect(page.getByText('3 items left')).toBeVisible();
|
||||
await expect(todoCount).toHaveText('3 items left');
|
||||
await expect(todoCount).toContainText('3');
|
||||
await expect(todoCount).toHaveText(/3/);
|
||||
|
||||
// Check all items in one call.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Mark all as completed', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to mark all items as completed', async ({ page }) => {
|
||||
// Complete all todos.
|
||||
await page.getByLabel('Mark all as complete').check();
|
||||
|
||||
// Ensure all todos have 'completed' class.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to clear the complete state of all items', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
// Check and then immediately uncheck.
|
||||
await toggleAll.check();
|
||||
await toggleAll.uncheck();
|
||||
|
||||
// Should be no completed classes.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
|
||||
});
|
||||
|
||||
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
await toggleAll.check();
|
||||
await expect(toggleAll).toBeChecked();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Uncheck first todo.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').uncheck();
|
||||
|
||||
// Reuse toggleAll locator and make sure its not checked.
|
||||
await expect(toggleAll).not.toBeChecked();
|
||||
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Assert the toggle all is checked again.
|
||||
await expect(toggleAll).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Item', () => {
|
||||
test('should allow me to mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
// Check first item.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
|
||||
// Check second item.
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await secondTodo.getByRole('checkbox').check();
|
||||
|
||||
// Assert completed class.
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).toHaveClass('completed');
|
||||
});
|
||||
|
||||
test('should allow me to un-mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
const firstTodoCheckbox = firstTodo.getByRole('checkbox');
|
||||
|
||||
await firstTodoCheckbox.check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await firstTodoCheckbox.uncheck();
|
||||
await expect(firstTodo).not.toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 0);
|
||||
});
|
||||
|
||||
test('should allow me to edit an item', async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const secondTodo = todoItems.nth(1);
|
||||
await secondTodo.dblclick();
|
||||
await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
// Explicitly assert the new text value.
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], 'buy some sausages', TODO_ITEMS[2]]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Editing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should hide other controls when editing', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item').nth(1);
|
||||
await todoItem.dblclick();
|
||||
await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
|
||||
await expect(
|
||||
todoItem.locator('label', {
|
||||
hasText: TODO_ITEMS[1],
|
||||
})
|
||||
).not.toBeVisible();
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should save edits on blur', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
|
||||
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], 'buy some sausages', TODO_ITEMS[2]]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should trim entered text', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], 'buy some sausages', TODO_ITEMS[2]]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should remove the item if an empty text string was entered', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should cancel edits on escape', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
|
||||
await expect(todoItems).toHaveText(TODO_ITEMS);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Counter', () => {
|
||||
test('should display the current number of todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count');
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
await expect(todoCount).toContainText('1');
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
await expect(todoCount).toContainText('2');
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Clear completed button', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
});
|
||||
|
||||
test('should display the correct text', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should remove completed items when clicked', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).getByRole('checkbox').check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(todoItems).toHaveCount(2);
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should be hidden when there are no items that are completed', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Persistence', () => {
|
||||
test('should persist its data', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
|
||||
await firstTodoCheck.check();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
|
||||
// Ensure there is 1 completed item.
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// Now reload.
|
||||
await page.reload();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Routing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
// make sure the app had a chance to save updated todos in storage
|
||||
// before navigating to a new view, otherwise the items can get lost :(
|
||||
// in some frameworks like Durandal
|
||||
await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
|
||||
});
|
||||
|
||||
test('should allow me to display active items', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should respect the back button', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await test.step('Showing all items', async () => {
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
await test.step('Showing active items', async () => {
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
});
|
||||
|
||||
await test.step('Showing completed items', async () => {
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
});
|
||||
|
||||
await expect(todoItem).toHaveCount(1);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should allow me to display completed items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('should allow me to display all items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should highlight the currently applied filter', async ({ page }) => {
|
||||
await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
|
||||
|
||||
//create locators for active and completed links
|
||||
const activeLink = page.getByRole('link', { name: 'Active' });
|
||||
const completedLink = page.getByRole('link', { name: 'Completed' });
|
||||
await activeLink.click();
|
||||
|
||||
// Page change - active items.
|
||||
await expect(activeLink).toHaveClass('selected');
|
||||
await completedLink.click();
|
||||
|
||||
// Page change - completed items.
|
||||
await expect(completedLink).toHaveClass('selected');
|
||||
});
|
||||
});
|
||||
|
||||
async function createDefaultTodos(page: Page) {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction((e) => {
|
||||
return JSON.parse(localStorage['react-todos']).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction((e) => {
|
||||
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkTodosInLocalStorage(page: Page, title: string) {
|
||||
return await page.waitForFunction((t) => {
|
||||
return JSON.parse(localStorage['react-todos'])
|
||||
.map((todo: any) => todo.title)
|
||||
.includes(t);
|
||||
}, title);
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
// tests/REQ0006/001_fresh-user-should-appears-sign-in-page.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
//
|
||||
import { CMS_HOST, HELLO } from '../_config/helloworld';
|
||||
import { TS_HELLO } from '../_test_set/helloworld';
|
||||
|
||||
test('fresh user should appears sign in page', async ({ page }) => {
|
||||
console.log({ HELLO, TS_HELLO });
|
||||
await page.goto(`${CMS_HOST}/dashboard`);
|
||||
|
||||
// Expect a title "to contain" a substring.
|
||||
await expect(page).toHaveTitle(/Sign in | Custom | Auth | demo cms/);
|
||||
});
|
||||
|
||||
// test('fresh user login', async ({ page }) => {
|
||||
// await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
// // Expect a title "to contain" a substring.
|
||||
// const emailField = page.getByPlaceholder('e.g. admin@123.com');
|
||||
|
||||
// await emailField.press('Enter');
|
||||
// });
|
||||
|
||||
// test('get started link', async ({ page }) => {
|
||||
// await page.goto('https://playwright.dev/');
|
||||
|
||||
// // Click the get started link.
|
||||
// await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// // Expects page to have a heading with the name of Installation.
|
||||
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
// });
|
@@ -0,0 +1,40 @@
|
||||
// tests/REQ0006/001_fresh-user-should-appears-sign-in-page.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
//
|
||||
import { CMS_HOST, HELLO } from '../_config/helloworld';
|
||||
import { TS_HELLO } from '../_test_set/helloworld';
|
||||
|
||||
test('user login', async ({ page }) => {
|
||||
console.log({ HELLO, TS_HELLO });
|
||||
|
||||
await page.goto(`${CMS_HOST}/dashboard`);
|
||||
await expect(page).toHaveTitle(/Sign in | Custom | Auth | demo cms/);
|
||||
|
||||
await page.getByPlaceholder('name@example.com').pressSequentially('user5@123.com');
|
||||
await page.getByPlaceholder('password').pressSequentially('user5@123.com');
|
||||
await page.waitForTimeout(1 * 1000);
|
||||
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
await page.waitForTimeout(1 * 1000);
|
||||
|
||||
await expect(page).toHaveTitle(/192.168.222.199:3000\/dashboard/);
|
||||
});
|
||||
|
||||
// test('fresh user login', async ({ page }) => {
|
||||
// await page.goto('http://192.168.222.199:3000/dashboard');
|
||||
|
||||
// // Expect a title "to contain" a substring.
|
||||
// const emailField = page.getByPlaceholder('e.g. admin@123.com');
|
||||
|
||||
// await emailField.press('Enter');
|
||||
// });
|
||||
|
||||
// test('get started link', async ({ page }) => {
|
||||
// await page.goto('https://playwright.dev/');
|
||||
|
||||
// // Click the get started link.
|
||||
// await page.getByRole('link', { name: 'Get started' }).click();
|
||||
|
||||
// // Expects page to have a heading with the name of Installation.
|
||||
// await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
||||
// });
|
@@ -0,0 +1,2 @@
|
||||
export const HELLO = 'WORLD';
|
||||
export const CMS_HOST = 'http://192.168.222.199:3000';
|
@@ -0,0 +1 @@
|
||||
export const TS_HELLO = 'WORLD';
|
11
003_test/002_mobile/01_test_seat/dc_up.sh
Executable file
11
003_test/002_mobile/01_test_seat/dc_up.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# docker compose build | tee dc_build.log
|
||||
|
||||
# docker compose kill
|
||||
# docker compose down
|
||||
docker compose up -d
|
||||
|
||||
echo "done"
|
32
003_test/002_mobile/01_test_seat/docker-compose.yml
Normal file
32
003_test/002_mobile/01_test_seat/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
name: 002_mobile
|
||||
|
||||
services:
|
||||
chromium:
|
||||
build: .
|
||||
security_opt:
|
||||
- seccomp:unconfined #optional
|
||||
environment:
|
||||
- TITLE=mobile-test-seat-1
|
||||
- FM_HOME=/app
|
||||
- PUID=1000 # default 911
|
||||
- PGID=1000 # default 911
|
||||
- TZ=Etc/Asia_HongKong
|
||||
- CHROME_CLI=https://www.gmail.com/ #optional
|
||||
# - CUSTOM_PORT=3000 # Internal port the container listens on for http if it needs to be swapped from the default 3000.
|
||||
- CUSTOM_HTTPS_PORT=3001 # Internal port the container listens on for https if it needs to be swapped from the default 3001.
|
||||
- NO_FULL=1 # Do n/ot autmatically fullscreen applications when using openbox.
|
||||
- LC_ALL=en_US.UTF-8 # Set the locale.
|
||||
- INSTALL_PACKAGES=fonts-noto-cjk
|
||||
ports:
|
||||
# - 6000:3000
|
||||
- 6002:3001 # https vnc ip
|
||||
- 9324:9323 # report ip
|
||||
volumes:
|
||||
- ./app:/app
|
||||
shm_size: '1gb'
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: 1
|
||||
reservations:
|
||||
cpus: 0.01
|
27
003_test/002_mobile/01_test_seat/dockerfile
Normal file
27
003_test/002_mobile/01_test_seat/dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
FROM ghcr.io/linuxserver/baseimage-kasmvnc:ubuntujammy
|
||||
|
||||
# set version label
|
||||
ARG BUILD_DATE
|
||||
ARG VERSION
|
||||
LABEL build_version="Linuxserver.io version:- ${VERSION} Build-date:- ${BUILD_DATE}"
|
||||
LABEL maintainer="thelamer"
|
||||
ARG DEBIAN_FRONTEND="noninteractive"
|
||||
|
||||
# title
|
||||
ENV TITLE=test-seat
|
||||
|
||||
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -qqy wget git curl
|
||||
|
||||
# Set up the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# ports and volumes
|
||||
EXPOSE 3000
|
||||
|
||||
VOLUME /config
|
||||
|
||||
COPY ./setup/ /setup
|
||||
RUN chmod +x /setup/*.sh
|
22
003_test/002_mobile/01_test_seat/setup/001_nvm.sh
Normal file
22
003_test/002_mobile/01_test_seat/setup/001_nvm.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "**** install nvm ****"
|
||||
|
||||
# Download and install Node.js (you may need to restart the terminal)
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
|
||||
|
||||
source ~/.nvm/nvm.sh
|
||||
|
||||
nvm install 20
|
||||
|
||||
nvm alias default 20
|
||||
|
||||
nvm use default
|
||||
|
||||
npm install -g yarn
|
||||
|
||||
echo "source ~/.nvm/nvm.sh" >>~/.bashrc
|
||||
|
||||
echo "install nvm done"
|
15
003_test/002_mobile/01_test_seat/setup/run_all.sh
Normal file
15
003_test/002_mobile/01_test_seat/setup/run_all.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
# ls -1 /setup/0*.sh | while read script; do
|
||||
# echo "Running $script"
|
||||
# bash "$script" &
|
||||
# done
|
||||
|
||||
/setup/001_nvm.sh &
|
||||
sudo /setup/002_google_chrome.sh &
|
||||
|
||||
wait
|
||||
|
||||
echo "done"
|
7
003_test/002_mobile/_GUIDELINES.md
Normal file
7
003_test/002_mobile/_GUIDELINES.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# GUIDELINES
|
||||
|
||||
this folder contains test and validation of the project
|
||||
|
||||
## highlight
|
||||
|
||||
- `01_test_seat` testing for mobile
|
9
003_test/_GUIDELINES.md
Normal file
9
003_test/_GUIDELINES.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# GUIDELINES
|
||||
|
||||
this folder contains test and validation of the project
|
||||
|
||||
## highlight
|
||||
|
||||
- `001_desktop` testing for desktop
|
||||
- `002_mobile` testing for mobile
|
||||
- `003_e2e` testing for end-to-end
|
14
003_test/default.code-workspace
Normal file
14
003_test/default.code-workspace
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "../001_documentation"
|
||||
},
|
||||
{
|
||||
"path": "../000_AI_WORKSPACE"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
BIN
003_test/desktop-test.gif
Normal file
BIN
003_test/desktop-test.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
BIN
003_test/mobile-test.gif
Normal file
BIN
003_test/mobile-test.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
5
003_test/scripts/dc_dev.sh
Normal file
5
003_test/scripts/dc_dev.sh
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
echo "done"
|
Reference in New Issue
Block a user