From ad0af8acc71a98826714dd427be9c54e51cbe673 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 11:38:59 +0000 Subject: [PATCH] Migrate FaceBookLoginTest to a Playwright TypeScript spec with PR CI --- .github/workflows/playwright-facebook.yml | 38 +++++++ e2e-playwright/.gitignore | 5 + e2e-playwright/README.md | 28 +++++ e2e-playwright/package-lock.json | 111 ++++++++++++++++++++ e2e-playwright/package.json | 16 +++ e2e-playwright/pages/facebook-login.page.ts | 45 ++++++++ e2e-playwright/playwright.config.ts | 24 +++++ e2e-playwright/tests/facebook-login.spec.ts | 53 ++++++++++ e2e-playwright/tsconfig.json | 13 +++ 9 files changed, 333 insertions(+) create mode 100644 .github/workflows/playwright-facebook.yml create mode 100644 e2e-playwright/.gitignore create mode 100644 e2e-playwright/README.md create mode 100644 e2e-playwright/package-lock.json create mode 100644 e2e-playwright/package.json create mode 100644 e2e-playwright/pages/facebook-login.page.ts create mode 100644 e2e-playwright/playwright.config.ts create mode 100644 e2e-playwright/tests/facebook-login.spec.ts create mode 100644 e2e-playwright/tsconfig.json diff --git a/.github/workflows/playwright-facebook.yml b/.github/workflows/playwright-facebook.yml new file mode 100644 index 0000000..9fce8f9 --- /dev/null +++ b/.github/workflows/playwright-facebook.yml @@ -0,0 +1,38 @@ +name: Playwright - Facebook login e2e + +on: + pull_request: + paths: + - 'e2e-playwright/**' + - '.github/workflows/playwright-facebook.yml' + +defaults: + run: + working-directory: e2e-playwright + +jobs: + facebook-login: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: e2e-playwright/package-lock.json + + - run: npm ci + + - run: npx playwright install --with-deps chromium + + - name: Run the migrated Facebook login spec + run: npx playwright test tests/facebook-login.spec.ts + + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: e2e-playwright/playwright-report/ + retention-days: 7 diff --git a/e2e-playwright/.gitignore b/e2e-playwright/.gitignore new file mode 100644 index 0000000..a3569bb --- /dev/null +++ b/e2e-playwright/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +playwright-report/ +test-results/ +blob-report/ +.playwright/ diff --git a/e2e-playwright/README.md b/e2e-playwright/README.md new file mode 100644 index 0000000..174195c --- /dev/null +++ b/e2e-playwright/README.md @@ -0,0 +1,28 @@ +# e2e-playwright + +Self-contained Playwright (TypeScript, chromium) project holding the modern port of the +legacy Selenium/TestNG `FaceBookLoginTest`. It has its own `package.json` and +`playwright.config.ts` and is independent of the Maven build; the Java suite is unchanged. + +```bash +cd e2e-playwright +npm ci +npx playwright install --with-deps chromium +npm test # or: npx playwright test tests/facebook-login.spec.ts +``` + +## What was migrated + +| Legacy (Java) | Here | +| --- | --- | +| `tests/FaceBookLoginTest.java` | `tests/facebook-login.spec.ts` | +| `pages/FacebookLoginPage.java` | `pages/facebook-login.page.ts` | +| `BaseTest` WebDriver setup, `WebDriverContext`, `PageinstancesFactory` | Playwright `page` fixture + config | +| `@FindBy(id = "email")` | `input[name="email"]` (the `id` is generated per render on current facebook.com) | + +The legacy test's final step is `Assert.assertTrue(false, "Login failed : Test failed")` - +an unconditional failure rather than a real check, with no credentials available to +actually log in. That step is preserved as a `test.fixme` in the spec with an explanatory +comment instead of being rewritten into an assertion that would pass vacuously. + +CI runs only this spec on pull requests via `.github/workflows/playwright-facebook.yml`. diff --git a/e2e-playwright/package-lock.json b/e2e-playwright/package-lock.json new file mode 100644 index 0000000..a16faf7 --- /dev/null +++ b/e2e-playwright/package-lock.json @@ -0,0 +1,111 @@ +{ + "name": "e2e-playwright", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "e2e-playwright", + "version": "1.0.0", + "devDependencies": { + "@playwright/test": "1.48.2", + "@types/node": "20.16.11", + "typescript": "5.6.3" + } + }, + "node_modules/@playwright/test": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "20.16.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.11.tgz", + "integrity": "sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/e2e-playwright/package.json b/e2e-playwright/package.json new file mode 100644 index 0000000..86de9ab --- /dev/null +++ b/e2e-playwright/package.json @@ -0,0 +1,16 @@ +{ + "name": "e2e-playwright", + "private": true, + "version": "1.0.0", + "description": "Playwright TypeScript migration of the legacy Selenium/TestNG FaceBookLoginTest", + "scripts": { + "test": "playwright test", + "test:headed": "playwright test --headed", + "report": "playwright show-report" + }, + "devDependencies": { + "@playwright/test": "1.48.2", + "@types/node": "20.16.11", + "typescript": "5.6.3" + } +} diff --git a/e2e-playwright/pages/facebook-login.page.ts b/e2e-playwright/pages/facebook-login.page.ts new file mode 100644 index 0000000..9d56606 --- /dev/null +++ b/e2e-playwright/pages/facebook-login.page.ts @@ -0,0 +1,45 @@ +import { Locator, Page, expect } from '@playwright/test'; + +/** + * Playwright port of the legacy Selenium page object + * src/test/java/example/example/pages/FacebookLoginPage.java. + * + * The legacy page object located the email field by `id=email`, which no longer + * exists: facebook.com now renders inputs with generated ids, so the field is + * addressed by its stable `name` attribute instead. + */ +export class FacebookLoginPage { + readonly emailInput: Locator; + readonly passwordInput: Locator; + readonly submitButton: Locator; + + constructor(private readonly page: Page) { + this.emailInput = page.locator('input[name="email"]'); + this.passwordInput = page.locator('input[name="pass"]'); + this.submitButton = page.locator('form input[type="submit"], form button[type="submit"]').first(); + } + + async goto(): Promise { + await this.page.goto('/', { waitUntil: 'domcontentloaded' }); + } + + async expectLoaded(): Promise { + await expect(this.emailInput).toBeVisible(); + await expect(this.passwordInput).toBeVisible(); + } + + async enterEmail(email: string): Promise { + await this.emailInput.fill(email); + return this; + } + + async enterPassword(password: string): Promise { + await this.passwordInput.fill(password); + return this; + } + + async clickSignIn(): Promise { + await this.passwordInput.press('Enter'); + await this.page.waitForLoadState('domcontentloaded'); + } +} diff --git a/e2e-playwright/playwright.config.ts b/e2e-playwright/playwright.config.ts new file mode 100644 index 0000000..933e963 --- /dev/null +++ b/e2e-playwright/playwright.config.ts @@ -0,0 +1,24 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './tests', + timeout: 60_000, + expect: { timeout: 15_000 }, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 1 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : [['list'], ['html', { open: 'never' }]], + use: { + baseURL: 'https://www.facebook.com', + trace: 'retain-on-failure', + screenshot: 'only-on-failure', + video: 'retain-on-failure', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/e2e-playwright/tests/facebook-login.spec.ts b/e2e-playwright/tests/facebook-login.spec.ts new file mode 100644 index 0000000..21dbc5b --- /dev/null +++ b/e2e-playwright/tests/facebook-login.spec.ts @@ -0,0 +1,53 @@ +import { expect, test } from '@playwright/test'; + +import { FacebookLoginPage } from '../pages/facebook-login.page'; + +/** + * Migration of the legacy Selenium/TestNG test + * src/test/java/example/example/tests/FaceBookLoginTest.java. + * + * The legacy test did three things: + * 1. navigated to https://www.facebook.com/ + * 2. filled email ("abc") and password ("abc") and submitted the login form + * 3. called `Assert.assertTrue(false, "Login failed : Test failed")` + * + * Steps 1 and 2 are ported below against the current facebook.com markup: the + * legacy `id=email` locator is gone (ids are generated per render), so the + * fields are addressed by their `name` attributes. + * + * Step 3 is a deliberate, unconditional failure in the legacy suite - it + * asserts a literal `false` and is not a statement about the application under + * test. There is also no real credential pair to log in with. It is therefore + * kept as an explicitly pending `test.fixme` rather than rewritten into an + * assertion that would trivially pass and hide the gap. + */ +test.describe('Facebook login (migrated from FaceBookLoginTest)', () => { + test('loads the login page and submits the credentials entered', async ({ page }) => { + const loginPage = new FacebookLoginPage(page); + + await loginPage.goto(); + await loginPage.expectLoaded(); + + await (await loginPage.enterEmail('abc')).enterPassword('abc'); + await expect(loginPage.emailInput).toHaveValue('abc'); + await expect(loginPage.passwordInput).toHaveValue('abc'); + + await loginPage.clickSignIn(); + + // The bogus credentials cannot authenticate; all that can honestly be + // asserted is that the submission was handled by facebook.com. + expect(new URL(page.url()).hostname).toContain('facebook.com'); + }); + + test.fixme( + 'reports a successful login - legacy assertion is an unconditional failure', + async () => { + // FaceBookLoginTest ends with `Assert.assertTrue(false, "Login failed : Test failed")`, + // so the legacy test can never pass. Verifying a successful login needs + // real credentials (and would require handling Facebook's bot checks), + // neither of which exists in this repository. Left pending on purpose: + // faking a passing assertion here would misrepresent coverage. + expect(true).toBe(false); + }, + ); +}); diff --git a/e2e-playwright/tsconfig.json b/e2e-playwright/tsconfig.json new file mode 100644 index 0000000..41e22f4 --- /dev/null +++ b/e2e-playwright/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "moduleResolution": "node", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "types": ["node"], + "noEmit": true + }, + "include": ["tests/**/*.ts", "pages/**/*.ts", "playwright.config.ts"] +}