Browse Source

feat(formating): Imrpoved linting rules regarding await by enforcing no floating promises

pull/3848/head
Muhammed Mustafa 2 years ago
parent
commit
2532f83c50
  1. 50
      scripts/playwright/.eslintrc.json
  2. 2
      scripts/playwright/.lintstagedrc.json
  3. 122
      scripts/playwright/package-lock.json
  4. 1
      scripts/playwright/package.json
  5. 2
      scripts/playwright/pages/Dashboard/common/Cell/index.ts
  6. 2
      scripts/playwright/setup/db.ts
  7. 1
      scripts/playwright/setup/index.ts
  8. 1
      scripts/playwright/setup/server.ts
  9. 375
      scripts/playwright/tests-examples/demo-todo-app.spec.ts
  10. 6
      scripts/playwright/tsconfig.json

50
scripts/playwright/.eslintrc.json

@ -14,7 +14,9 @@
"playwright-report", "playwright-report",
"output", "output",
"dist", "dist",
"nc" "nc",
"tsconfig.json",
".eslintrc.json"
], ],
"plugins": [ "plugins": [
"import", "import",
@ -26,7 +28,8 @@
"plugin:eslint-comments/recommended", "plugin:eslint-comments/recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:import/typescript", "plugin:import/typescript",
"plugin:prettier/recommended" "plugin:prettier/recommended",
"plugin:json/recommended"
], ],
"globals": { "globals": {
"BigInt": true, "BigInt": true,
@ -37,7 +40,7 @@
"localStorage": true "localStorage": true
}, },
"rules": { "rules": {
"require-await": "error", "@typescript-eslint/no-floating-promises": ["error"],
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"eslint-comments/disable-enable-pair": [ "eslint-comments/disable-enable-pair": [
"error", "error",
@ -55,7 +58,6 @@
], ],
"@typescript-eslint/no-this-alias": "off", "@typescript-eslint/no-this-alias": "off",
// todo: enable
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
@ -64,45 +66,5 @@
"no-empty": "off", "no-empty": "off",
"@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"import/order": "off" "import/order": "off"
// "@typescript-eslint/member-ordering": [
// "warn" ,
// {
// "default": {
// "memberTypes": [ "static-field",
// "public-field",
// "instance-field",
// "protected-field",
// "private-field",
// "abstract-field",
//
// "public-static-field",
// "protected-static-field",
// "private-static-field",
// "public-instance-field",
// "public-decorated-field",
// "public-abstract-field",
// "protected-instance-field",
// "protected-decorated-field",
// "protected-abstract-field",
// "private-instance-field",
// "private-decorated-field",
// "private-abstract-field",
//
//
//
// "constructor",
//
// "public-static-method",
// "protected-static-method",
// "private-static-method",
// "public-method",
// "protected-method",
// "private-method"
// ]
// }
// }
// ]
} }
} }

2
scripts/playwright/.lintstagedrc.json

@ -1,3 +1,3 @@
{ {
"./setup/index.ts": "npx eslint --fix" "**/*.{ts,tsx,js,json}": "npx eslint --fix"
} }

122
scripts/playwright/package-lock.json generated

@ -25,6 +25,7 @@
"eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2", "eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0", "eslint-plugin-import": "^2.22.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"husky": "^8.0.1", "husky": "^8.0.1",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
@ -1599,6 +1600,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eslint-plugin-json": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz",
"integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==",
"dev": true,
"dependencies": {
"lodash": "^4.17.21",
"vscode-json-languageservice": "^4.1.6"
},
"engines": {
"node": ">=12.0"
}
},
"node_modules/eslint-plugin-prettier": { "node_modules/eslint-plugin-prettier": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
@ -2719,6 +2733,12 @@
"json5": "lib/cli.js" "json5": "lib/cli.js"
} }
}, },
"node_modules/jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"node_modules/levn": { "node_modules/levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -2851,6 +2871,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -4632,6 +4658,43 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/vscode-json-languageservice": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz",
"integrity": "sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==",
"dev": true,
"dependencies": {
"jsonc-parser": "^3.0.0",
"vscode-languageserver-textdocument": "^1.0.3",
"vscode-languageserver-types": "^3.16.0",
"vscode-nls": "^5.0.0",
"vscode-uri": "^3.0.3"
}
},
"node_modules/vscode-languageserver-textdocument": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz",
"integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==",
"dev": true
},
"node_modules/vscode-languageserver-types": {
"version": "3.17.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
"integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==",
"dev": true
},
"node_modules/vscode-nls": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
"integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==",
"dev": true
},
"node_modules/vscode-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz",
"integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==",
"dev": true
},
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@ -5942,6 +6005,16 @@
} }
} }
}, },
"eslint-plugin-json": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz",
"integrity": "sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==",
"dev": true,
"requires": {
"lodash": "^4.17.21",
"vscode-json-languageservice": "^4.1.6"
}
},
"eslint-plugin-prettier": { "eslint-plugin-prettier": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
@ -6697,6 +6770,12 @@
"minimist": "^1.2.0" "minimist": "^1.2.0"
} }
}, },
"jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"levn": { "levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -6790,6 +6869,12 @@
} }
} }
}, },
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lodash.merge": { "lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -8131,6 +8216,43 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
}, },
"vscode-json-languageservice": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.2.1.tgz",
"integrity": "sha512-xGmv9QIWs2H8obGbWg+sIPI/3/pFgj/5OWBhNzs00BkYQ9UaB2F6JJaGB/2/YOZJ3BvLXQTC4Q7muqU25QgAhA==",
"dev": true,
"requires": {
"jsonc-parser": "^3.0.0",
"vscode-languageserver-textdocument": "^1.0.3",
"vscode-languageserver-types": "^3.16.0",
"vscode-nls": "^5.0.0",
"vscode-uri": "^3.0.3"
}
},
"vscode-languageserver-textdocument": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.7.tgz",
"integrity": "sha512-bFJH7UQxlXT8kKeyiyu41r22jCZXG8kuuVVA33OEJn1diWOZK5n8zBSPZFHVBOu8kXZ6h0LIRhf5UnCo61J4Hg==",
"dev": true
},
"vscode-languageserver-types": {
"version": "3.17.2",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz",
"integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==",
"dev": true
},
"vscode-nls": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz",
"integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==",
"dev": true
},
"vscode-uri": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz",
"integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==",
"dev": true
},
"which": { "which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

1
scripts/playwright/package.json

@ -26,6 +26,7 @@
"eslint-plugin-functional": "^3.0.2", "eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0", "eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-json": "^3.1.0",
"husky": "^8.0.1", "husky": "^8.0.1",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",

2
scripts/playwright/pages/Dashboard/common/Cell/index.ts

@ -103,7 +103,7 @@ export class CellPageObject extends BasePage {
const chips = cell.locator('.chips > .chip'); const chips = cell.locator('.chips > .chip');
// verify chip count & contents // verify chip count & contents
if (count) expect(chips).toHaveCount(count); if (count) await expect(chips).toHaveCount(count);
// verify only the elements that are passed in // verify only the elements that are passed in
for (let i = 0; i < value.length; ++i) { for (let i = 0; i < value.length; ++i) {

2
scripts/playwright/setup/db.ts

@ -9,7 +9,7 @@ const isSqlite = (context: NcContext) => context.dbType === 'sqlite';
const isPg = (context: NcContext) => context.dbType === 'pg'; const isPg = (context: NcContext) => context.dbType === 'pg';
const mysql = require('mysql2'); const mysql = require('mysql2');
// eslint-disable-next-line require-await
const mysqlExec = async query => { const mysqlExec = async query => {
// creates a new mysql connection using credentials from cypress.json env's // creates a new mysql connection using credentials from cypress.json env's
const connection = mysql.createConnection({ const connection = mysql.createConnection({

1
scripts/playwright/setup/index.ts

@ -24,7 +24,6 @@ const setup = async ({ page, isEmptyProject }: { page: Page; isEmptyProject?: bo
const token = response.data.token; const token = response.data.token;
await page.addInitScript( await page.addInitScript(
// eslint-disable-next-line require-await
async ({ token }) => { async ({ token }) => {
try { try {
let initialLocalStorage = {}; let initialLocalStorage = {};

1
scripts/playwright/setup/server.ts

@ -8,7 +8,6 @@ import bodyParser from 'body-parser';
let request = []; let request = [];
// eslint-disable-next-line require-await
async function makeServer() { async function makeServer() {
const app = express(); const app = express();
app.use(bodyParser.json()); app.use(bodyParser.json());

375
scripts/playwright/tests-examples/demo-todo-app.spec.ts

@ -1,375 +0,0 @@
import { expect, type Page, test } 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'];
test.describe('New Todo', () => {
test('should allow me to add todo items', async ({ page }) => {
// Create 1st todo.
await page.locator('.new-todo').fill(TODO_ITEMS[0]);
await page.locator('.new-todo').press('Enter');
// Make sure the list only has one todo item.
await expect(page.locator('.view label')).toHaveText([TODO_ITEMS[0]]);
// Create 2nd todo.
await page.locator('.new-todo').fill(TODO_ITEMS[1]);
await page.locator('.new-todo').press('Enter');
// Make sure the list now has two todo items.
await expect(page.locator('.view label')).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 one todo item.
await page.locator('.new-todo').fill(TODO_ITEMS[0]);
await page.locator('.new-todo').press('Enter');
// Check that input is empty.
await expect(page.locator('.new-todo')).toBeEmpty();
await checkNumberOfTodosInLocalStorage(page, 1);
});
test('should append new items to the bottom of the list', async ({ page }) => {
// Create 3 items.
await createDefaultTodos(page);
// Check test using different methods.
await expect(page.locator('.todo-count')).toHaveText('3 items left');
await expect(page.locator('.todo-count')).toContainText('3');
await expect(page.locator('.todo-count')).toHaveText(/3/);
// Check all items in one call.
await expect(page.locator('.view label')).toHaveText(TODO_ITEMS);
await checkNumberOfTodosInLocalStorage(page, 3);
});
test('should show #main and #footer when items added', async ({ page }) => {
await page.locator('.new-todo').fill(TODO_ITEMS[0]);
await page.locator('.new-todo').press('Enter');
await expect(page.locator('.main')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await checkNumberOfTodosInLocalStorage(page, 1);
});
});
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.locator('.toggle-all').check();
// Ensure all todos have 'completed' class.
await expect(page.locator('.todo-list li')).toHaveClass(['completed', 'completed', 'completed']);
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
});
test('should allow me to clear the complete state of all items', async ({ page }) => {
// Check and then immediately uncheck.
await page.locator('.toggle-all').check();
await page.locator('.toggle-all').uncheck();
// Should be no completed classes.
await expect(page.locator('.todo-list li')).toHaveClass(['', '', '']);
});
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
const toggleAll = page.locator('.toggle-all');
await toggleAll.check();
await expect(toggleAll).toBeChecked();
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
// Uncheck first todo.
const firstTodo = page.locator('.todo-list li').nth(0);
await firstTodo.locator('.toggle').uncheck();
// Reuse toggleAll locator and make sure its not checked.
await expect(toggleAll).not.toBeChecked();
await firstTodo.locator('.toggle').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 two items.
for (const item of TODO_ITEMS.slice(0, 2)) {
await page.locator('.new-todo').fill(item);
await page.locator('.new-todo').press('Enter');
}
// Check first item.
const firstTodo = page.locator('.todo-list li').nth(0);
await firstTodo.locator('.toggle').check();
await expect(firstTodo).toHaveClass('completed');
// Check second item.
const secondTodo = page.locator('.todo-list li').nth(1);
await expect(secondTodo).not.toHaveClass('completed');
await secondTodo.locator('.toggle').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 two items.
for (const item of TODO_ITEMS.slice(0, 2)) {
await page.locator('.new-todo').fill(item);
await page.locator('.new-todo').press('Enter');
}
const firstTodo = page.locator('.todo-list li').nth(0);
const secondTodo = page.locator('.todo-list li').nth(1);
await firstTodo.locator('.toggle').check();
await expect(firstTodo).toHaveClass('completed');
await expect(secondTodo).not.toHaveClass('completed');
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await firstTodo.locator('.toggle').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.locator('.todo-list li');
const secondTodo = todoItems.nth(1);
await secondTodo.dblclick();
await expect(secondTodo.locator('.edit')).toHaveValue(TODO_ITEMS[1]);
await secondTodo.locator('.edit').fill('buy some sausages');
await secondTodo.locator('.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.locator('.todo-list li').nth(1);
await todoItem.dblclick();
await expect(todoItem.locator('.toggle')).not.toBeVisible();
await expect(todoItem.locator('label')).not.toBeVisible();
await checkNumberOfTodosInLocalStorage(page, 3);
});
test('should save edits on blur', async ({ page }) => {
const todoItems = page.locator('.todo-list li');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).locator('.edit').fill('buy some sausages');
await todoItems.nth(1).locator('.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.locator('.todo-list li');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).locator('.edit').fill(' buy some sausages ');
await todoItems.nth(1).locator('.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.locator('.todo-list li');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).locator('.edit').fill('');
await todoItems.nth(1).locator('.edit').press('Enter');
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
});
test('should cancel edits on escape', async ({ page }) => {
const todoItems = page.locator('.todo-list li');
await todoItems.nth(1).dblclick();
await todoItems.nth(1).locator('.edit').press('Escape');
await expect(todoItems).toHaveText(TODO_ITEMS);
});
});
test.describe('Counter', () => {
test('should display the current number of todo items', async ({ page }) => {
await page.locator('.new-todo').fill(TODO_ITEMS[0]);
await page.locator('.new-todo').press('Enter');
await expect(page.locator('.todo-count')).toContainText('1');
await page.locator('.new-todo').fill(TODO_ITEMS[1]);
await page.locator('.new-todo').press('Enter');
await expect(page.locator('.todo-count')).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.locator('.clear-completed')).toHaveText('Clear completed');
});
test('should remove completed items when clicked', async ({ page }) => {
const todoItems = page.locator('.todo-list li');
await todoItems.nth(1).locator('.toggle').check();
await page.locator('.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.locator('.clear-completed').click();
await expect(page.locator('.clear-completed')).toBeHidden();
});
});
test.describe('Persistence', () => {
test('should persist its data', async ({ page }) => {
for (const item of TODO_ITEMS.slice(0, 2)) {
await page.locator('.new-todo').fill(item);
await page.locator('.new-todo').press('Enter');
}
const todoItems = page.locator('.todo-list li');
await todoItems.nth(0).locator('.toggle').check();
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
await expect(todoItems).toHaveClass(['completed', '']);
// Ensure there is 1 completed item.
checkNumberOfCompletedTodosInLocalStorage(page, 1);
// Now reload.
await page.reload();
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
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 }) => {
await page.locator('.todo-list li .toggle').nth(1).check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.locator('.filters >> text=Active').click();
await expect(page.locator('.todo-list li')).toHaveCount(2);
await expect(page.locator('.todo-list li')).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
});
test('should respect the back button', async ({ page }) => {
await page.locator('.todo-list li .toggle').nth(1).check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await test.step('Showing all items', async () => {
await page.locator('.filters >> text=All').click();
await expect(page.locator('.todo-list li')).toHaveCount(3);
});
await test.step('Showing active items', async () => {
await page.locator('.filters >> text=Active').click();
});
await test.step('Showing completed items', async () => {
await page.locator('.filters >> text=Completed').click();
});
await expect(page.locator('.todo-list li')).toHaveCount(1);
await page.goBack();
await expect(page.locator('.todo-list li')).toHaveCount(2);
await page.goBack();
await expect(page.locator('.todo-list li')).toHaveCount(3);
});
test('should allow me to display completed items', async ({ page }) => {
await page.locator('.todo-list li .toggle').nth(1).check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.locator('.filters >> text=Completed').click();
await expect(page.locator('.todo-list li')).toHaveCount(1);
});
test('should allow me to display all items', async ({ page }) => {
await page.locator('.todo-list li .toggle').nth(1).check();
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
await page.locator('.filters >> text=Active').click();
await page.locator('.filters >> text=Completed').click();
await page.locator('.filters >> text=All').click();
await expect(page.locator('.todo-list li')).toHaveCount(3);
});
test('should highlight the currently applied filter', async ({ page }) => {
await expect(page.locator('.filters >> text=All')).toHaveClass('selected');
await page.locator('.filters >> text=Active').click();
// Page change - active items.
await expect(page.locator('.filters >> text=Active')).toHaveClass('selected');
await page.locator('.filters >> text=Completed').click();
// Page change - completed items.
await expect(page.locator('.filters >> text=Completed')).toHaveClass('selected');
});
});
async function createDefaultTodos(page: Page) {
for (const item of TODO_ITEMS) {
await page.locator('.new-todo').fill(item);
await page.locator('.new-todo').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);
}

6
scripts/playwright/tsconfig.json

@ -60,11 +60,13 @@
"**/*.json", "**/*.json",
"**/*.d.ts", "**/*.d.ts",
"**/*.js", "**/*.js",
"playwright.config.ts" "playwright.config.ts",
".*.json",
"*.json",
], ],
"exclude": [ "exclude": [
"node_modules/**", "node_modules/**",
"node_modules", "node_modules",
], ],
"compileOnSave": false "compileOnSave": false,
} }

Loading…
Cancel
Save