From 86000172d7ee8f9dbd492ad3daeb63158f618a74 Mon Sep 17 00:00:00 2001 From: Ryuji Eguchi Date: Thu, 30 Nov 2023 04:44:09 +1100 Subject: [PATCH 1/5] rm example files provided by playwright --- .github/workflows/playwright.yml | 28 -- e2e-tests-examples/demo-todo-app.spec.ts | 483 ----------------------- e2e/example.spec.ts | 20 - 3 files changed, 531 deletions(-) delete mode 100644 .github/workflows/playwright.yml delete mode 100644 e2e-tests-examples/demo-todo-app.spec.ts delete mode 100644 e2e/example.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index 03a0fc817..000000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,28 +0,0 @@ -# TODO: enable this workflow after smoke test is done -# name: Playwright Tests -# on: -# push: -# branches: [ main, master ] -# pull_request: -# branches: [ main, master ] -# jobs: -# test: -# timeout-minutes: 60 -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v3 -# - uses: actions/setup-node@v3 -# with: -# node-version: 18 -# - name: Install dependencies -# run: npm ci -# - name: Install Playwright Browsers -# run: npx playwright install --with-deps -# - name: Run Playwright tests -# run: npx playwright test -# - uses: actions/upload-artifact@v3 -# if: always() -# with: -# name: playwright-report -# path: playwright-report/ -# retention-days: 30 diff --git a/e2e-tests-examples/demo-todo-app.spec.ts b/e2e-tests-examples/demo-todo-app.spec.ts deleted file mode 100644 index 9627adb6f..000000000 --- a/e2e-tests-examples/demo-todo-app.spec.ts +++ /dev/null @@ -1,483 +0,0 @@ -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', -] - -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) -} diff --git a/e2e/example.spec.ts b/e2e/example.spec.ts deleted file mode 100644 index f30b24aba..000000000 --- a/e2e/example.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -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() -}) From b99c43d31a3a3551f626be89c115d82f8eebfcc4 Mon Sep 17 00:00:00 2001 From: Ryuji Eguchi Date: Thu, 30 Nov 2023 04:44:32 +1100 Subject: [PATCH 2/5] shorten playwright timeout --- playwright.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/playwright.config.ts b/playwright.config.ts index aa70662dc..411e40a7f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -74,4 +74,5 @@ export default defineConfig({ // url: 'http://127.0.0.1:3000', // reuseExistingServer: !process.env.CI, // }, + timeout: 5 * 1000, }) From 843bc3e518daa93a9c10fba9b204bd321e718864 Mon Sep 17 00:00:00 2001 From: Ryuji Eguchi Date: Thu, 30 Nov 2023 04:51:56 +1100 Subject: [PATCH 3/5] add e2e/production.spec.ts --- e2e/production.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 e2e/production.spec.ts diff --git a/e2e/production.spec.ts b/e2e/production.spec.ts new file mode 100644 index 000000000..63a1d87c8 --- /dev/null +++ b/e2e/production.spec.ts @@ -0,0 +1,7 @@ +import { test, expect } from '@playwright/test' + +test('has title', async ({ page }) => { + await page.goto('https://steexp.com/') + + await expect(page).toHaveTitle(/Stellar Explorer/) +}) From 52d030424e77685bc60ccff4b494f83dea877e42 Mon Sep 17 00:00:00 2001 From: Ryuji Eguchi Date: Thu, 30 Nov 2023 04:58:29 +1100 Subject: [PATCH 4/5] add playwright-production.yml --- .github/workflows/playwright-production.yml | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/playwright-production.yml diff --git a/.github/workflows/playwright-production.yml b/.github/workflows/playwright-production.yml new file mode 100644 index 000000000..5a1ec81a7 --- /dev/null +++ b/.github/workflows/playwright-production.yml @@ -0,0 +1,27 @@ +name: Run e2e tests against production + +on: + schedule: + - cron: '0 * * * *' + +jobs: + test_schedule: + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run e2e tests + run: npm run test:e2e -- e2e/production.spec.ts + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report-production + path: playwright-report-production/ + retention-days: 30 From 9ec84a91dbd97c48f13a54e73ebb3c2ed5523351 Mon Sep 17 00:00:00 2001 From: Ryuji Eguchi Date: Thu, 30 Nov 2023 09:54:42 +1100 Subject: [PATCH 5/5] check all pages in e2e/production.spec.ts --- e2e/production.spec.ts | 48 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/e2e/production.spec.ts b/e2e/production.spec.ts index 63a1d87c8..78c717332 100644 --- a/e2e/production.spec.ts +++ b/e2e/production.spec.ts @@ -1,7 +1,51 @@ import { test, expect } from '@playwright/test' -test('has title', async ({ page }) => { +test('top page', async ({ page }) => { await page.goto('https://steexp.com/') + await expect(page).toHaveTitle('Stellar Explorer | Home') +}) + +test('operations', async ({ page }) => { + await page.goto('https://steexp.com/operations') + await expect(page).toHaveTitle('Stellar Explorer | Operations') +}) + +test('transactions', async ({ page }) => { + await page.goto('https://steexp.com/txs') + await expect(page).toHaveTitle('Stellar Explorer | Transactions') +}) + +test('ledgers', async ({ page }) => { + await page.goto('https://steexp.com/ledgers') + await expect(page).toHaveTitle('Stellar Explorer | Ledgers') +}) + +test('assets', async ({ page }) => { + await page.goto('https://steexp.com/assets') + await expect(page).toHaveTitle('Stellar Explorer | Assets') +}) + +test('anchors', async ({ page }) => { + await page.goto('https://steexp.com/anchors') + await expect(page).toHaveTitle('Stellar Explorer | Anchors') +}) + +test('exchanges', async ({ page }) => { + await page.goto('https://steexp.com/exchanges') + await expect(page).toHaveTitle('Stellar Explorer | Exchanges') +}) + +test('effects', async ({ page }) => { + await page.goto('https://steexp.com/effects') + await expect(page).toHaveTitle('Stellar Explorer | Effects') +}) + +test('payments', async ({ page }) => { + await page.goto('https://steexp.com/payments') + await expect(page).toHaveTitle('Stellar Explorer | Payments') +}) - await expect(page).toHaveTitle(/Stellar Explorer/) +test('trades', async ({ page }) => { + await page.goto('https://steexp.com/trades') + await expect(page).toHaveTitle('Stellar Explorer | Trades') })