From e9fa6a037557467f3c1dc667834ddca02d33a21c Mon Sep 17 00:00:00 2001 From: inc-man Date: Mon, 7 Apr 2025 09:20:28 +0200 Subject: [PATCH 1/7] new method to manually log in with an existing identity --- src/page-objects/InternetIdentityPage.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index bdf11d9..d648c2a 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -153,4 +153,26 @@ export class InternetIdentityPage { await iiPage.waitForEvent('close'); expect(iiPage.isClosed()).toBe(true); }; + manuallySignInWithIdentity = async ({ + selector, + identity + }: { + selector?: string; + identity: number; + }): Promise => { + const iiPagePromise = this.context.waitForEvent('page'); + + await this.page.locator(selector ?? '[data-tid=login-button]').click(); + + const iiPage = await iiPagePromise; + await expect(iiPage).toHaveTitle('Internet Identity'); + + await iiPage.locator(selector ?? '[data-role="more-options"]').click(); + await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); + await iiPage.locator(selector ?? '[data-action="continue"]').click(); + await iiPage.waitForEvent('close'); + expect(iiPage.isClosed()).toBe(true); + }; } + + From 384dd3400aa42328bdf46ee0af12f91a2cbfd46d Mon Sep 17 00:00:00 2001 From: inc-man Date: Mon, 7 Apr 2025 11:43:56 +0200 Subject: [PATCH 2/7] revert previous changes and extend existing signInWithIdentity --- src/page-objects/InternetIdentityPage.ts | 31 ++++++------------------ 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index d648c2a..371a9a7 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -149,30 +149,15 @@ export class InternetIdentityPage { const iiPage = await iiPagePromise; await expect(iiPage).toHaveTitle('Internet Identity'); - await iiPage.locator(`[data-anchor-id='${identity}']`).click(); - await iiPage.waitForEvent('close'); - expect(iiPage.isClosed()).toBe(true); - }; - manuallySignInWithIdentity = async ({ - selector, - identity - }: { - selector?: string; - identity: number; - }): Promise => { - const iiPagePromise = this.context.waitForEvent('page'); - - await this.page.locator(selector ?? '[data-tid=login-button]').click(); - - const iiPage = await iiPagePromise; - await expect(iiPage).toHaveTitle('Internet Identity'); - - await iiPage.locator(selector ?? '[data-role="more-options"]').click(); - await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); - await iiPage.locator(selector ?? '[data-action="continue"]').click(); + const identityLocator = iiPage.locator(`[data-anchor-id='${identity}']`); + if (await identityLocator.count() > 0) { + await iiPage.locator(`[data-anchor-id='${identity}']`).click(); + } else { + await iiPage.locator(selector ?? '[data-role="more-options"]').click(); + await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); + await iiPage.locator(selector ?? '[data-action="continue"]').click(); + } await iiPage.waitForEvent('close'); expect(iiPage.isClosed()).toBe(true); }; } - - From fc7caa2650f910e960fde49179b5ab93393fd3df Mon Sep 17 00:00:00 2001 From: inc-man Date: Mon, 7 Apr 2025 11:54:54 +0200 Subject: [PATCH 3/7] lint --- src/page-objects/InternetIdentityPage.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index 371a9a7..55adad7 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -150,12 +150,12 @@ export class InternetIdentityPage { await expect(iiPage).toHaveTitle('Internet Identity'); const identityLocator = iiPage.locator(`[data-anchor-id='${identity}']`); - if (await identityLocator.count() > 0) { + if (await identityLocator.count() === 1) { await iiPage.locator(`[data-anchor-id='${identity}']`).click(); } else { - await iiPage.locator(selector ?? '[data-role="more-options"]').click(); + await iiPage.locator('[data-role="more-options"]').click(); await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); - await iiPage.locator(selector ?? '[data-action="continue"]').click(); + await iiPage.locator('[data-action="continue"]').click(); } await iiPage.waitForEvent('close'); expect(iiPage.isClosed()).toBe(true); From 18b9eedbf2ebb72f1b483a2e4d223a1f4ae0ad57 Mon Sep 17 00:00:00 2001 From: inc-man Date: Tue, 8 Apr 2025 12:58:13 +0200 Subject: [PATCH 4/7] added new test --- e2e/login.spec.ts | 17 +++++ src/page-objects/InternetIdentityPage.ts | 82 ++++++++++++++++++++++-- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/e2e/login.spec.ts b/e2e/login.spec.ts index 0daa4e2..edf4df2 100644 --- a/e2e/login.spec.ts +++ b/e2e/login.spec.ts @@ -1,3 +1,4 @@ +import test from 'node:test'; import {testWithII} from '../src'; const loginSelector = '#login'; @@ -45,3 +46,19 @@ testWithII.describe('with selector', () => { await iiPage.signInWithIdentity({identity, selector: loginSelector}); }); }); + +testWithII.describe.serial('Identity sign in tests', () => { + let createdIdentity: number; + + testWithII('Create Identity and capture it', async ({page, iiPage}) => { + await page.goto('/'); + createdIdentity = await iiPage.createNewIdentity(); + }); + + testWithII('Sign in using identity from list or fallback manually', async ({page, iiPage}) => { + await page.goto('/'); + await iiPage.manuallySignInWithIdentity({identity: createdIdentity}); + await page.locator(logoutSelector).click(); + await iiPage.manuallySignInWithIdentity({identity: createdIdentity}); + }); +}); diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index 55adad7..948bc43 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -149,13 +149,85 @@ export class InternetIdentityPage { const iiPage = await iiPagePromise; await expect(iiPage).toHaveTitle('Internet Identity'); - const identityLocator = iiPage.locator(`[data-anchor-id='${identity}']`); - if (await identityLocator.count() === 1) { - await iiPage.locator(`[data-anchor-id='${identity}']`).click(); - } else { - await iiPage.locator('[data-role="more-options"]').click(); + await iiPage.locator(`[data-anchor-id='${identity}']`).click(); + await iiPage.waitForEvent('close'); + expect(iiPage.isClosed()).toBe(true); + }; + + createNewIdentity = async (params?: { + selector?: string; + captcha?: boolean; + }): Promise => { + const iiPagePromise = this.context.waitForEvent('page'); + + await this.page.locator(params?.selector ?? '[data-tid=login-button]').click(); + + const iiPage = await iiPagePromise; + await expect(iiPage).toHaveTitle('Internet Identity'); + + await iiPage.locator('#registerButton').click(); + await iiPage.locator('[data-action=construct-identity]').click(); + + if (params?.captcha === true) { + await iiPage.locator('input#captchaInput').fill('a', {timeout: 10000}); + await iiPage.locator('#confirmRegisterButton').click(); + } + + const identityText = await iiPage.locator('#userNumber').textContent(); + console.log('Identity text:', identityText); + const createdIdentity = parseInt(identityText!); + expect(createdIdentity).not.toBeNull(); + return createdIdentity; + } + + manuallySignInWithIdentity = async ({ + selector, + identity + }: { + selector?: string; + identity: number; + }): Promise => { + const iiPagePromise = this.context.waitForEvent('page'); + + await this.page.locator(selector ?? '[data-tid=login-button]').click(); + + const iiPage = await iiPagePromise; + await expect(iiPage).toHaveTitle('Internet Identity'); + + const identityLocator = iiPage.locator(`[data-anchor-id="${identity}"]`); + const firstTimeLocator = iiPage.locator('#loginButton'); + const fallbackLocator = iiPage.locator('[data-role="more-options"]'); + + const waitOptions: { state: 'visible'; timeout: number } = { state: 'visible', timeout: 30000 }; + + const identityPromise = identityLocator.waitFor(waitOptions) + .then(() => 'identity' as const) + .catch(() => 'none' as const); + const firstTimePromise = firstTimeLocator.waitFor(waitOptions) + .then(() => 'firsttime' as const) + .catch(() => 'none' as const); + const fallbackPromise = fallbackLocator.waitFor(waitOptions) + .then(() => 'fallback' as const) + .catch(() => 'none' as const); + + const result: 'identity' | 'firsttime' | 'fallback' | 'none' = await Promise.race([ + identityPromise, + fallbackPromise, + firstTimePromise, + ]); + + if (result === 'identity') { + await identityLocator.first().click({ force: true}); + } else if (result === 'firsttime') { + await firstTimeLocator.click({ force: true }); await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); await iiPage.locator('[data-action="continue"]').click(); + } else if (result === 'fallback') { + await fallbackLocator.click({ force: true }); + await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); + await iiPage.locator('[data-action="continue"]').click(); + } else { + throw new Error('No locator found for identity, first time, or fallback'); } await iiPage.waitForEvent('close'); expect(iiPage.isClosed()).toBe(true); From 023895ab1368ff441a0322ee0bede250d286a5f8 Mon Sep 17 00:00:00 2001 From: inc-man Date: Tue, 8 Apr 2025 12:59:59 +0200 Subject: [PATCH 5/7] lint --- src/page-objects/InternetIdentityPage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index 948bc43..4f8250c 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -174,7 +174,6 @@ export class InternetIdentityPage { } const identityText = await iiPage.locator('#userNumber').textContent(); - console.log('Identity text:', identityText); const createdIdentity = parseInt(identityText!); expect(createdIdentity).not.toBeNull(); return createdIdentity; From af6cba852094c1810cb63f0cb40b4d6d1f172c54 Mon Sep 17 00:00:00 2001 From: inc-man Date: Tue, 8 Apr 2025 14:10:42 +0200 Subject: [PATCH 6/7] lint --- e2e/login.spec.ts | 6 +++--- src/page-objects/InternetIdentityPage.ts | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/e2e/login.spec.ts b/e2e/login.spec.ts index edf4df2..056eca7 100644 --- a/e2e/login.spec.ts +++ b/e2e/login.spec.ts @@ -47,15 +47,15 @@ testWithII.describe('with selector', () => { }); }); -testWithII.describe.serial('Identity sign in tests', () => { +testWithII.describe.serial('create identity and manual sign-in', () => { let createdIdentity: number; - testWithII('Create Identity and capture it', async ({page, iiPage}) => { + testWithII('create identity and capture it', async ({page, iiPage}) => { await page.goto('/'); createdIdentity = await iiPage.createNewIdentity(); }); - testWithII('Sign in using identity from list or fallback manually', async ({page, iiPage}) => { + testWithII('sign in using identity from list or fallback manually', async ({page, iiPage}) => { await page.goto('/'); await iiPage.manuallySignInWithIdentity({identity: createdIdentity}); await page.locator(logoutSelector).click(); diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index 4f8250c..628af32 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -194,20 +194,22 @@ export class InternetIdentityPage { await expect(iiPage).toHaveTitle('Internet Identity'); const identityLocator = iiPage.locator(`[data-anchor-id="${identity}"]`); - const firstTimeLocator = iiPage.locator('#loginButton'); + const initialLoginLocator = iiPage.locator('#loginButton'); const fallbackLocator = iiPage.locator('[data-role="more-options"]'); - const waitOptions: { state: 'visible'; timeout: number } = { state: 'visible', timeout: 30000 }; + const waitOptions: { state: 'visible'; timeout: number } = { state: 'visible', timeout: 10000 }; + + type RaceResult = 'identity' | 'firsttime' | 'fallback' | 'none'; const identityPromise = identityLocator.waitFor(waitOptions) - .then(() => 'identity' as const) - .catch(() => 'none' as const); - const firstTimePromise = firstTimeLocator.waitFor(waitOptions) - .then(() => 'firsttime' as const) - .catch(() => 'none' as const); + .then(() => 'identity' as RaceResult) + .catch(() => 'none' as RaceResult); + const firstTimePromise = initialLoginLocator.waitFor(waitOptions) + .then(() => 'firsttime' as RaceResult) + .catch(() => 'none' as RaceResult); const fallbackPromise = fallbackLocator.waitFor(waitOptions) - .then(() => 'fallback' as const) - .catch(() => 'none' as const); + .then(() => 'fallback' as RaceResult) + .catch(() => 'none' as RaceResult); const result: 'identity' | 'firsttime' | 'fallback' | 'none' = await Promise.race([ identityPromise, @@ -218,7 +220,7 @@ export class InternetIdentityPage { if (result === 'identity') { await identityLocator.first().click({ force: true}); } else if (result === 'firsttime') { - await firstTimeLocator.click({ force: true }); + await initialLoginLocator.click({ force: true }); await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); await iiPage.locator('[data-action="continue"]').click(); } else if (result === 'fallback') { From c0b341e4cd6d704b014e199a10a4ada0bacdf0db Mon Sep 17 00:00:00 2001 From: inc-man Date: Wed, 9 Apr 2025 11:36:59 +0200 Subject: [PATCH 7/7] changes for clarity --- src/page-objects/InternetIdentityPage.ts | 78 ++++++++++++++---------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/page-objects/InternetIdentityPage.ts b/src/page-objects/InternetIdentityPage.ts index 628af32..808dfca 100644 --- a/src/page-objects/InternetIdentityPage.ts +++ b/src/page-objects/InternetIdentityPage.ts @@ -193,42 +193,54 @@ export class InternetIdentityPage { const iiPage = await iiPagePromise; await expect(iiPage).toHaveTitle('Internet Identity'); - const identityLocator = iiPage.locator(`[data-anchor-id="${identity}"]`); - const initialLoginLocator = iiPage.locator('#loginButton'); - const fallbackLocator = iiPage.locator('[data-role="more-options"]'); + const existingIdentityLoginLocator = iiPage.locator(`[data-anchor-id="${identity}"]`); + const firstRunManualLoginLocator = iiPage.locator('#loginButton'); + const moreOptionsLoginLocator = iiPage.locator('[data-role="more-options"]'); const waitOptions: { state: 'visible'; timeout: number } = { state: 'visible', timeout: 10000 }; - type RaceResult = 'identity' | 'firsttime' | 'fallback' | 'none'; - - const identityPromise = identityLocator.waitFor(waitOptions) - .then(() => 'identity' as RaceResult) - .catch(() => 'none' as RaceResult); - const firstTimePromise = initialLoginLocator.waitFor(waitOptions) - .then(() => 'firsttime' as RaceResult) - .catch(() => 'none' as RaceResult); - const fallbackPromise = fallbackLocator.waitFor(waitOptions) - .then(() => 'fallback' as RaceResult) - .catch(() => 'none' as RaceResult); - - const result: 'identity' | 'firsttime' | 'fallback' | 'none' = await Promise.race([ - identityPromise, - fallbackPromise, - firstTimePromise, - ]); - - if (result === 'identity') { - await identityLocator.first().click({ force: true}); - } else if (result === 'firsttime') { - await initialLoginLocator.click({ force: true }); - await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); - await iiPage.locator('[data-action="continue"]').click(); - } else if (result === 'fallback') { - await fallbackLocator.click({ force: true }); - await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); - await iiPage.locator('[data-action="continue"]').click(); - } else { - throw new Error('No locator found for identity, first time, or fallback'); + type RaceResult = 'existingIdentityLogin' | 'firstRunManualLogin' | 'moreOptionsLogin' | 'none'; + + async function getRaceResult( + locator: { waitFor: (options: any) => Promise }, + successResult: RaceResult + ): Promise { + try { + await locator.waitFor(waitOptions); + return successResult; + } catch { + return 'none'; + } + } + + const identityPromise = getRaceResult(existingIdentityLoginLocator, 'existingIdentityLogin'); + const firstTimePromise = getRaceResult(firstRunManualLoginLocator, 'firstRunManualLogin'); + const fallbackPromise = getRaceResult(moreOptionsLoginLocator, 'moreOptionsLogin'); + + const result: RaceResult = await Promise.race([ + identityPromise, + fallbackPromise, + firstTimePromise, + ]); + + switch(result) { + case 'existingIdentityLogin': + await existingIdentityLoginLocator.first().click({ force: true }); + break; + + case 'firstRunManualLogin': + case 'moreOptionsLogin': { + const locator = + result === 'firstRunManualLogin' ? firstRunManualLoginLocator : moreOptionsLoginLocator; + await locator.click({ force: true }); + await iiPage.fill('input[data-role="anchor-input"]', identity.toString()); + await iiPage.locator('[data-action="continue"]').click(); + break; + } + default: + throw new Error( + 'No locator found for Buttons: existingIdentity, firstRunManualLogin or moreOptionsLogin' + ); } await iiPage.waitForEvent('close'); expect(iiPage.isClosed()).toBe(true);