diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..c4dfaec --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,51 @@ +# .github/workflows/playwright.yml +name: Playwright Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + browser: [firefox, webkit, googlechrome] + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Set Up Node.js + uses: actions/setup-node@v3 + with: + node-version: '16' + + - name: Install Dependencies + run: npm install + + - name: Run Playwright Tests + run: npx playwright test + + - name: Upload Playwright Report + uses: actions/upload-artifact@v3 + with: + name: playwright-report + path: playwright-report + + - name: Upload Videos + uses: actions/upload-artifact@v3 + with: + name: test-videos + path: test-results/videos + + - name: Upload Screenshots + uses: actions/upload-artifact@v3 + with: + name: test-screenshots + path: test-results/screenshots + + - name: Upload Traces + uses: actions/upload-artifact@v3 + with: + name: test-traces + path: test-results/traces diff --git a/package.json b/package.json new file mode 100644 index 0000000..f36a3e6 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "the-internet-automation-typescript-playwright", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "playwright test", + "test:headed": "playwright test --headed", + "test:debug": "playwright test --debug" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@playwright/test": "^1.45.3", + "@types/node": "^20.14.12", + "playwright": "^1.45.3", + "ts-node": "^10.9.2", + "typescript": "^5.5.4" + } +} diff --git a/pages/ABTestingPage.ts b/pages/ABTestingPage.ts new file mode 100644 index 0000000..beaea95 --- /dev/null +++ b/pages/ABTestingPage.ts @@ -0,0 +1,21 @@ +import { Page, Locator } from '@playwright/test'; + +export class ABTestingPage { + readonly page: Page; + readonly heading: Locator; + URL: string; + + constructor(page: Page, URL: string) { + this.page = page; + this.heading = page.locator('h3'); + this.URL = URL; + } + + async goto() { + await this.page.goto(this.URL); + } + + async getHeadingText() { + return await this.heading.textContent(); + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..10a07b7 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests', + + timeout: 30000, + use: { + headless: true, + viewport: { width: 1280, height: 720 }, + ignoreHTTPSErrors: true, + video: 'retain-on-failure', + screenshot: 'only-on-failure', + trace: 'on', + baseURL: 'https://the-internet.herokuapp.com', + + }, + reporter: [ + ['list'], + ['html', { outputFolder: 'playwright-report' }], + ['json', { outputFile: 'test-results.json' }], + ['junit', { outputFile: 'test-results.xml' }], + ], + + retries: 2, + +}); diff --git a/test-file.txt b/test-file.txt new file mode 100644 index 0000000..e69de29 diff --git a/test-results.json b/test-results.json new file mode 100644 index 0000000..842b85e --- /dev/null +++ b/test-results.json @@ -0,0 +1,125 @@ +{ + "config": { + "configFile": "/Users/mnabil/the-internet-automation-typescript-playwright/playwright.config.ts", + "rootDir": "/Users/mnabil/the-internet-automation-typescript-playwright/tests", + "forbidOnly": false, + "fullyParallel": false, + "globalSetup": null, + "globalTeardown": null, + "globalTimeout": 0, + "grep": {}, + "grepInvert": null, + "maxFailures": 0, + "metadata": { + "actualWorkers": 1 + }, + "preserveOutput": "always", + "reporter": [ + [ + "list", + null + ], + [ + "html", + { + "outputFolder": "playwright-report" + } + ], + [ + "json", + { + "outputFile": "test-results.json" + } + ], + [ + "junit", + { + "outputFile": "test-results.xml" + } + ] + ], + "reportSlowTests": { + "max": 5, + "threshold": 15000 + }, + "quiet": false, + "projects": [ + { + "outputDir": "/Users/mnabil/the-internet-automation-typescript-playwright/test-results", + "repeatEach": 1, + "retries": 2, + "metadata": {}, + "id": "", + "name": "", + "testDir": "/Users/mnabil/the-internet-automation-typescript-playwright/tests", + "testIgnore": [], + "testMatch": [ + "**/*.@(spec|test).?(c|m)[jt]s?(x)" + ], + "timeout": 30000 + } + ], + "shard": null, + "updateSnapshots": "missing", + "version": "1.45.3", + "workers": 4, + "webServer": null + }, + "suites": [ + { + "title": "form_authentication.spec.ts", + "file": "form_authentication.spec.ts", + "column": 0, + "line": 0, + "specs": [ + { + "title": "Form Authentication", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 30000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "", + "projectName": "", + "results": [ + { + "workerIndex": 0, + "status": "passed", + "duration": 2896, + "errors": [], + "stdout": [], + "stderr": [], + "retry": 0, + "startTime": "2024-07-24T16:32:35.505Z", + "attachments": [ + { + "name": "trace", + "contentType": "application/zip", + "path": "/Users/mnabil/the-internet-automation-typescript-playwright/test-results/form_authentication-Form-Authentication/trace.zip" + } + ] + } + ], + "status": "expected" + } + ], + "id": "6df759fb1ef0caef90bd-a6c447046936b2600450", + "file": "form_authentication.spec.ts", + "line": 3, + "column": 5 + } + ] + } + ], + "errors": [], + "stats": { + "startTime": "2024-07-24T16:32:35.135Z", + "duration": 4046.454, + "expected": 1, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + } +} \ No newline at end of file diff --git a/test-results.xml b/test-results.xml new file mode 100644 index 0000000..5b6a616 --- /dev/null +++ b/test-results.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/ab_testing.spec.ts b/tests/ab_testing.spec.ts new file mode 100644 index 0000000..73897c8 --- /dev/null +++ b/tests/ab_testing.spec.ts @@ -0,0 +1,9 @@ +import { test, expect } from '@playwright/test'; +import { ABTestingPage } from '../pages/ABTestingPage'; + +test('A/B Testing', async ({ page }) => { + const abTestingPage = new ABTestingPage(page, '/abtest'); + await abTestingPage.goto(); + const headingText = await abTestingPage.getHeadingText(); + await expect(headingText).toContain('A/B Test'); +}); diff --git a/tests/checkboxes.spec.ts b/tests/checkboxes.spec.ts new file mode 100644 index 0000000..9e89d7a --- /dev/null +++ b/tests/checkboxes.spec.ts @@ -0,0 +1,12 @@ +import { test, expect } from '@playwright/test'; + +test('Checkboxes', async ({ page }) => { + await page.goto('/checkboxes'); + + const checkbox1 = page.locator('input[type="checkbox"]').first(); + const checkbox2 = page.locator('input[type="checkbox"]').nth(1); + + await expect(checkbox1).not.toBeChecked(); + await expect(checkbox2).toBeChecked(); + +}); diff --git a/tests/disappearing_elements.spec.ts b/tests/disappearing_elements.spec.ts new file mode 100644 index 0000000..9b6e48f --- /dev/null +++ b/tests/disappearing_elements.spec.ts @@ -0,0 +1,13 @@ +import { test, expect } from '@playwright/test'; + +test('Disappearing Elements', async ({ page }) => { + await page.goto('/disappearing_elements'); + const links = await page.locator('ul > li > a'); + const expectedLinks = ['Home', 'About', 'Contact Us', 'Portfolio', 'Gallery']; + for (const link of expectedLinks) { + const linkElement = links.locator(`text=${link}`); + if (await linkElement.count() > 0) { + await expect(linkElement).toBeVisible(); + } + } +}); diff --git a/tests/drag_and_drop.spec.ts b/tests/drag_and_drop.spec.ts new file mode 100644 index 0000000..0efdef5 --- /dev/null +++ b/tests/drag_and_drop.spec.ts @@ -0,0 +1,10 @@ +import { test, expect } from '@playwright/test'; + +test('Drag and Drop', async ({ page }) => { + await page.goto('/drag_and_drop'); + const source = page.locator('#column-a'); + const target = page.locator('#column-b'); + await source.dragTo(target); + await expect(source).toHaveText('B'); + await expect(target).toHaveText('A'); +}); diff --git a/tests/dropdown.spec.ts b/tests/dropdown.spec.ts new file mode 100644 index 0000000..e86e9a6 --- /dev/null +++ b/tests/dropdown.spec.ts @@ -0,0 +1,10 @@ +import { test, expect } from '@playwright/test'; + +test('Dropdown', async ({ page }) => { + await page.goto('/dropdown'); + const dropdown = page.locator('#dropdown'); + await dropdown.selectOption('1'); + await expect(dropdown).toHaveValue('1'); + await dropdown.selectOption('2'); + await expect(dropdown).toHaveValue('2'); +}); diff --git a/tests/dynamic_controls.spec.ts b/tests/dynamic_controls.spec.ts new file mode 100644 index 0000000..9270a8a --- /dev/null +++ b/tests/dynamic_controls.spec.ts @@ -0,0 +1,21 @@ +import { test, expect } from '@playwright/test'; + +test('Dynamic Controls', async ({ page }) => { + await page.goto('/dynamic_controls'); + + // Checkbox + const checkbox = page.locator('#checkbox'); + await expect(checkbox).toBeVisible(); + await page.click('#checkbox-example button'); + await expect(checkbox).not.toBeVisible(); + await page.click('#checkbox-example button'); + await expect(checkbox).toBeVisible(); + + // Input + const input = page.locator('#input-example input'); + await expect(input).toBeDisabled(); + await page.click('#input-example button'); + await expect(input).toBeEnabled(); + await page.click('#input-example button'); + await expect(input).toBeDisabled(); +}); diff --git a/tests/dynamic_loading.spec.ts b/tests/dynamic_loading.spec.ts new file mode 100644 index 0000000..862a28d --- /dev/null +++ b/tests/dynamic_loading.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('Dynamic Loading', async ({ page }) => { + await page.goto('/dynamic_loading/1'); + await page.click('#start button'); + const finish = await page.locator('#finish'); + await expect(finish).toContainText('Hello World!'); +}); diff --git a/tests/file_download.spec.ts b/tests/file_download.spec.ts new file mode 100644 index 0000000..e90a9e3 --- /dev/null +++ b/tests/file_download.spec.ts @@ -0,0 +1,36 @@ +import { test, expect } from '@playwright/test'; +import * as fs from 'fs'; +import * as path from 'path'; + +test('File Download with Specified Filename', async ({ page }) => { + await page.goto('/download'); + + const [download] = await Promise.all([ + page.waitForEvent('download'), + page.click('a[href*="download/"]') // Adjust the selector if necessary + ]); + + // Get the suggested filename from the download + const suggestedFilename = download.suggestedFilename(); + console.log(`Suggested filename: ${suggestedFilename}`); + + // Define a custom filename + const customFilename = 'test-file.txt'; + const downloadDir = './downloads'; + const customFilePath = path.join(downloadDir, customFilename); + + // Ensure the downloads directory exists + if (!fs.existsSync(downloadDir)) { + fs.mkdirSync(downloadDir); + } + + // Save the download to a temporary path + const tempFilePath = await download.path(); + + // Move the file to the custom path + fs.renameSync(tempFilePath!, customFilePath); + + // Verify the file exists at the custom path + expect(fs.existsSync(customFilePath)).toBeTruthy(); + console.log(`File downloaded and renamed to: ${customFilePath}`); +}); diff --git a/tests/file_upload.spec.ts b/tests/file_upload.spec.ts new file mode 100644 index 0000000..77d29c9 --- /dev/null +++ b/tests/file_upload.spec.ts @@ -0,0 +1,11 @@ +import { test, expect } from '@playwright/test'; +import * as path from 'path'; + +test('File Upload', async ({ page }) => { + await page.goto('/upload'); + const filePath = path.resolve('test-file.txt'); + await page.setInputFiles('input[type=file]', filePath); + await page.click('#file-submit'); + const uploadedFile = page.locator('#uploaded-files'); + await expect(uploadedFile).toHaveText('test-file.txt'); +}); diff --git a/tests/floating_menu.spec.ts b/tests/floating_menu.spec.ts new file mode 100644 index 0000000..8af7a38 --- /dev/null +++ b/tests/floating_menu.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('Floating Menu', async ({ page }) => { + await page.goto('/floating_menu'); + await page.evaluate(() => window.scrollBy(0, window.innerHeight)); + const floatingMenu = page.locator('#menu'); + await expect(floatingMenu).toBeVisible(); +}); diff --git a/tests/form_authentication.spec.ts b/tests/form_authentication.spec.ts new file mode 100644 index 0000000..3ff914b --- /dev/null +++ b/tests/form_authentication.spec.ts @@ -0,0 +1,11 @@ +import { test, expect } from '@playwright/test'; + +test('Form Authentication', async ({ page }) => { + + await page.goto('/login'); + await page.fill('#username', 'tomsmith'); + await page.fill('#password', 'SuperSecretPassword!'); + await page.click('button[type="submit"]'); + const flash = page.locator('#flash'); + await expect(flash).toContainText('You logged into a secure area!'); +}); diff --git a/tests/frames.spec.ts b/tests/frames.spec.ts new file mode 100644 index 0000000..bc150ce --- /dev/null +++ b/tests/frames.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test('Frames', async ({ page }) => { + await page.goto('/frames'); + await page.click('a[href="/nested_frames"]'); + + // Accessing the top frame + const frameTop = page.frameLocator('frame[name="frame-top"]'); + + // Accessing the left frame within the top frame + const frameLeft = frameTop.frameLocator('frame[name="frame-left"]'); + + // Getting the text content of the left frame's body + const bodyText = await frameLeft.locator('body').textContent(); + + // Assert that the text content is 'LEFT' + expect(bodyText?.trim()).toBe('LEFT'); +}); diff --git a/tests/geolocation.spec.ts b/tests/geolocation.spec.ts new file mode 100644 index 0000000..e7e8ec3 --- /dev/null +++ b/tests/geolocation.spec.ts @@ -0,0 +1,10 @@ +import { test, expect } from '@playwright/test'; + +test('Geolocation', async ({ page, context }) => { + await context.grantPermissions(['geolocation']); + await context.setGeolocation({ latitude: 28.6139, longitude: 77.209 }); + await page.goto('/geolocation'); + await page.click('button'); + const geoLocationText = await page.locator('#lat-value').textContent(); + await expect(geoLocationText).toContain('28.6139'); +}); diff --git a/tests/horizontal_slider.spec.ts b/tests/horizontal_slider.spec.ts new file mode 100644 index 0000000..72cdc10 --- /dev/null +++ b/tests/horizontal_slider.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test'; + +test('Horizontal Slider', async ({ page }) => { + await page.goto('/horizontal_slider'); + + const slider = page.locator('input[type="range"]'); + + // Get the bounding box of the slider + const sliderBoundingBox = await slider.boundingBox(); + + if (sliderBoundingBox) { + // Calculate the new position + const sliderWidth = sliderBoundingBox.width; + const newPosition = sliderBoundingBox.x + (sliderWidth * (3.5 / 5)); // Assuming the slider ranges from 0 to 5 + + // Move the mouse to the starting position of the slider + await page.mouse.move(sliderBoundingBox.x + sliderWidth / 2, sliderBoundingBox.y + sliderBoundingBox.height / 2); + + // Drag the slider to the new position + await page.mouse.down(); + await page.mouse.move(newPosition, sliderBoundingBox.y + sliderBoundingBox.height / 2); + await page.mouse.up(); + + // Verify the value + await expect(slider).toHaveValue('3.5'); + } +}); diff --git a/tests/hovers.spec.ts b/tests/hovers.spec.ts new file mode 100644 index 0000000..d8cd53c --- /dev/null +++ b/tests/hovers.spec.ts @@ -0,0 +1,9 @@ +import { test, expect } from '@playwright/test'; + +test('Hovers', async ({ page }) => { + await page.goto('/hovers'); + const figure = page.locator('.figure').nth(0); + await figure.hover(); + const caption = figure.locator('.figcaption'); + await expect(caption).toBeVisible(); +}); diff --git a/tests/infinite_scroll.spec.ts b/tests/infinite_scroll.spec.ts new file mode 100644 index 0000000..d848c1c --- /dev/null +++ b/tests/infinite_scroll.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('Infinite Scroll', async ({ page }) => { + await page.goto('/infinite_scroll'); + const content = page.locator('.jscroll-added'); + await page.evaluate(() => window.scrollBy(0, window.innerHeight)); + await expect(content).toHaveCount(2); +}); diff --git a/tests/javascript_alerts.spec.ts b/tests/javascript_alerts.spec.ts new file mode 100644 index 0000000..8050ee3 --- /dev/null +++ b/tests/javascript_alerts.spec.ts @@ -0,0 +1,30 @@ +import { test, expect } from '@playwright/test'; + +test('JavaScript Alerts', async ({ page }) => { + await page.goto('/javascript_alerts'); + + // Set up a single dialog event listener + page.on('dialog', async dialog => { + const message = dialog.message(); + if (message.includes('I am a JS Alert')) { + await dialog.accept(); + } else if (message.includes('I am a JS Confirm')) { + await dialog.accept(); + } else if (message.includes('I am a JS prompt')) { + await dialog.accept('Hello!'); + } + }); + + // Alert + await page.click('button[onclick="jsAlert()"]'); + const result = page.locator('#result'); + await expect(result).toHaveText('You successfully clicked an alert'); + + // Confirm + await page.click('button[onclick="jsConfirm()"]'); + await expect(result).toHaveText('You clicked: Ok'); + + // Prompt + await page.click('button[onclick="jsPrompt()"]'); + await expect(result).toHaveText('You entered: Hello!'); +}); diff --git a/tests/javascript_onload_event_error.spec.ts b/tests/javascript_onload_event_error.spec.ts new file mode 100644 index 0000000..cf19e65 --- /dev/null +++ b/tests/javascript_onload_event_error.spec.ts @@ -0,0 +1,7 @@ +import { test, expect } from '@playwright/test'; + +test('JavaScript onload event error', async ({ page }) => { + await page.goto('/javascript_error'); + const errorText = await page.locator('body').textContent(); + expect(errorText).toContain('This page has a JavaScript error in the onload event.'); +}); diff --git a/tests/jquery_ui_menus.spec.ts b/tests/jquery_ui_menus.spec.ts new file mode 100644 index 0000000..627516c --- /dev/null +++ b/tests/jquery_ui_menus.spec.ts @@ -0,0 +1,23 @@ +import { test, expect } from '@playwright/test'; + +test('JQuery UI Menus', async ({ page }) => { + await page.goto('/jqueryui/menu'); + + // Hover over the "Enabled" menu item to reveal its submenu + const enabledMenu = page.locator('text=Enabled'); + await enabledMenu.hover(); + + // Wait for the "Downloads" submenu item to be visible + const downloadsMenu = page.locator('text=Downloads'); + await downloadsMenu.waitFor({ state: 'visible' }); + await downloadsMenu.hover(); + + // Wait for the "PDF" item to be visible and then click on it + const pdfMenuItem = page.locator('text=PDF'); + await pdfMenuItem.waitFor({ state: 'visible' }); + await pdfMenuItem.click(); + + // Verify that the result is as expected (you may need to adjust the expected URL or result based on actual behavior) + // This is an example. Adjust the URL or result check as needed. + await expect(page).toHaveURL('https://the-internet.herokuapp.com/jqueryui/menu'); +}); diff --git a/tests/key_presses.spec.ts b/tests/key_presses.spec.ts new file mode 100644 index 0000000..b8458a5 --- /dev/null +++ b/tests/key_presses.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('Key Presses', async ({ page }) => { + await page.goto('/key_presses'); + await page.keyboard.press('A'); + const result = page.locator('#result'); + await expect(result).toHaveText('You entered: A'); +}); diff --git a/tests/multiple_windows.spec.ts b/tests/multiple_windows.spec.ts new file mode 100644 index 0000000..a2a100e --- /dev/null +++ b/tests/multiple_windows.spec.ts @@ -0,0 +1,11 @@ +import { test, expect } from '@playwright/test'; + +test('Multiple Windows', async ({ page, context }) => { + await page.goto('/windows'); + const [newPage] = await Promise.all([ + context.waitForEvent('page'), + page.click('a[href="/windows/new"]') + ]); + await newPage.waitForLoadState(); + await expect(newPage).toHaveTitle('New Window'); +}); diff --git a/tests/notification_messages.spec.ts b/tests/notification_messages.spec.ts new file mode 100644 index 0000000..b0167f1 --- /dev/null +++ b/tests/notification_messages.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test('Notification Messages', async ({ page }) => { + await page.goto('/notification_message_rendered'); + + // Click on the link to trigger the notification message + await page.click('a[href="/notification_message"]'); + + // Wait for the notification message to appear + const message = page.locator('#flash'); + + // Wait for the notification to be visible + await message.waitFor({ state: 'visible' }); + + // Verify the message contains the expected text + await expect(message).toHaveText(/Action (unsuccessful|successful)/i); +}); diff --git a/tests/shifting_content.spec.ts b/tests/shifting_content.spec.ts new file mode 100644 index 0000000..b9554ea --- /dev/null +++ b/tests/shifting_content.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('Shifting Content', async ({ page }) => { + await page.goto('/shifting_content'); + await page.click('a[href="/shifting_content/menu"]'); + const menuItem = await page.locator('ul li:nth-child(1)'); + await expect(menuItem).toBeVisible(); +}); diff --git a/tests/slow_resources.spec.ts b/tests/slow_resources.spec.ts new file mode 100644 index 0000000..27b57dd --- /dev/null +++ b/tests/slow_resources.spec.ts @@ -0,0 +1,7 @@ +import { test, expect } from '@playwright/test'; + +test('Slow Resources', async ({ page }) => { + await page.goto('/slow'); + const heading = await page.locator('h3'); + await expect(heading).toContainText('Slow Resources'); +}); diff --git a/tests/sortable_data_tables.spec.ts b/tests/sortable_data_tables.spec.ts new file mode 100644 index 0000000..72ee148 --- /dev/null +++ b/tests/sortable_data_tables.spec.ts @@ -0,0 +1,42 @@ +import { test, expect } from '@playwright/test'; + +test('Sortable Data Tables', async ({ page }) => { + // Step 1: Navigate to the tables page + await page.goto('/tables'); + + // Helper function to get table rows and verify data + async function getTableData(selector: string) { + // Extract text content from each row and return as an array + return await page.locator(`${selector} tbody tr`).allTextContents(); + } + + // Step 2: Click the column header to sort the table (e.g., "Last Name" column) + const headerLocator = page.locator('table#table1 thead th').nth(1); // Assuming sorting by the second column (Last Name) + await headerLocator.click(); + + // Wait for sorting to be applied (adjust if necessary) + await page.waitForTimeout(1000); + + // Step 3: Verify the sorting by checking data order in the sorted column + const tableDataBefore = await getTableData('table#table1'); + + // Click the header again to sort in reverse order + await headerLocator.click(); + + // Wait again for sorting to be applied + await page.waitForTimeout(1000); + + const tableDataAfter = await getTableData('table#table1'); + + // Extract sorted column values from before and after sorting + const extractColumnData = (data: string[]) => data.map(row => row.split(/\s{2,}/)[1]); // Assuming columns are separated by multiple spaces + + const columnDataBefore = extractColumnData(tableDataBefore); + const columnDataAfter = extractColumnData(tableDataAfter); + + // Check if the data has changed after sorting + const isSortedAscending = columnDataBefore.join(',') !== columnDataAfter.join(','); + const isSortedDescending = columnDataAfter.join(',') === columnDataBefore.reverse().join(','); + + expect(isSortedAscending || isSortedDescending).toBe(true); +}); diff --git a/tests/status_codes.spec.ts b/tests/status_codes.spec.ts new file mode 100644 index 0000000..8438ddb --- /dev/null +++ b/tests/status_codes.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('Status Codes', async ({ page }) => { + await page.goto('/status_codes'); + await page.click('a[href="status_codes/200"]'); + const statusCode = await page.locator('h3'); + await expect(statusCode).toHaveText('Status Codes'); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c6b7fac --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "CommonJS", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "rootDir": "./" + }, + "include": ["**/*.ts"] +}