Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions e2e/davinci-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
<a href="https://www.typescriptlang.org/" target="_blank">
<img src="./public/typescript.svg" class="logo vanilla" alt="TypeScript logo" />
</a>
<form id="form">
<div class="error-div"></div>
</form>
<form id="form"></form>
</div>
</div>
<script type="module" src="main.ts"></script>
Expand Down
10 changes: 5 additions & 5 deletions e2e/davinci-app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ const urlParams = new URLSearchParams(window.location.search);
formEl.innerHTML = '';

const clientInfo = davinciClient.getClient();
//const clientInfo = node.client;

let formName = '';

Expand All @@ -191,11 +190,12 @@ const urlParams = new URLSearchParams(window.location.search);

const error = davinciClient.getError();
if (error) {
formEl.appendChild(document.createElement('div')).setAttribute('id', 'error-div');
const errorDiv = formEl.querySelector('#error-div');
if (errorDiv && clientInfo?.status === 'continue') {
const errorDiv = document.createElement('div');
formEl.appendChild(errorDiv).setAttribute('id', 'error-div');
if (errorDiv && clientInfo?.status === 'error') {
errorDiv.style.color = 'red';
errorDiv.innerHTML = `
<div>${davinciClient.getError()?.message}</div>
<p><strong>Error</strong>: ${davinciClient.getError()?.message}</p>
`;
}
Comment thread
cerebrl marked this conversation as resolved.
}
Expand Down
12 changes: 12 additions & 0 deletions e2e/davinci-app/server-configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,16 @@ export const serverConfigs: Record<string, DaVinciConfig> = {
'https://auth.pingone.ca/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/.well-known/openid-configuration',
},
},
/**
* Polling
*/
'31a587ce-9aa4-4f36-a09f-78cd8a0a74a0': {
clientId: '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0',
redirectUri: window.location.origin + '/',
scope: 'openid profile email revoke',
serverConfig: {
wellknown:
'https://auth.pingone.ca/356a254c-cba3-4ade-be1a-860136e8df01/as/.well-known/openid-configuration',
},
},
};
4 changes: 4 additions & 0 deletions e2e/davinci-app/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ input {
color: #888;
}

.error {
color: red;
}

button {
border-radius: 8px;
border: 1px solid transparent;
Expand Down
178 changes: 178 additions & 0 deletions e2e/davinci-suites/src/polling.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2026 Ping Identity Corporation. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
import { expect, test } from '@playwright/test';
import { asyncEvents } from './utils/async-events.js';

test.describe('Challenge Polling', () => {
test('should succeed when opening magic link', async ({ page, browser }) => {
const clientId = '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0';
const davinciPolicy = 'f40b544a4dfb575daa0cf5e9487c206a';
const { navigate } = asyncEvents(page);
await navigate(`/?clientId=${clientId}&acr_values=${davinciPolicy}`);

await expect(page.url()).toBe(
`http://localhost:5829/?clientId=${clientId}&acr_values=${davinciPolicy}`,
);

await page.getByRole('button', { name: 'Sign On' }).click();
await expect(page.getByRole('heading', { name: 'Polling' })).toBeVisible();

// Get magic link
const linkLocator = page.getByText('Number Challenge https://auth.pingone');
await expect(linkLocator).toBeVisible();

const linkLocatorText = await linkLocator.innerText();
const magicLink = linkLocatorText.split('Number Challenge ')[1];
expect(magicLink.startsWith('https://auth.pingone'));

// Start polling
await page.getByRole('button', { name: 'Start polling' }).click();
await expect(page.getByText('Polling...')).toBeVisible();

// Go to magic link in another browser to complete challenge
const newContext = await browser.newContext();
const newPage = await newContext.newPage();
await newPage.goto(magicLink);
await expect(newPage.getByText('Close me')).toBeVisible();
await newContext.close();

// Check for success
await expect(page.getByText('Message: approved')).toBeVisible();
});

test('should timeout when retries are exhausted', async ({ page }) => {
const clientId = '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0';
const davinciPolicy = 'f40b544a4dfb575daa0cf5e9487c206a';
const { navigate } = asyncEvents(page);
await navigate(`/?clientId=${clientId}&acr_values=${davinciPolicy}`);

await expect(page.url()).toBe(
`http://localhost:5829/?clientId=${clientId}&acr_values=${davinciPolicy}`,
);

await page.getByRole('button', { name: 'Sign On' }).click();
await expect(page.getByRole('heading', { name: 'Polling' })).toBeVisible();

// Track poll retries
let numPollRequests = 0;
page.on('request', (request) => {
const method = request.method();
const requestUrl = request.url();

if (method === 'POST' && requestUrl.includes('/status')) {
numPollRequests++;
}
});

// Start polling
await page.getByRole('button', { name: 'Start polling' }).click();
await expect(page.getByText('Polling...')).toBeVisible();

// Wait for timeout
const pollInterval = 2000; // milliseconds
const maxRetries = 5;
await expect(page.getByText('Error: timedOut')).toBeVisible({
timeout: 2 * pollInterval * maxRetries,
});

// Check max retry count
expect(numPollRequests === maxRetries);
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Incorrect assertion - test will always pass.

The assertion expect(numPollRequests === maxRetries) evaluates to a boolean and passes it to expect(), which will always succeed. This doesn't actually assert the condition.

🐛 Proposed fix
     // Check max retry count
-    expect(numPollRequests === maxRetries);
+    expect(numPollRequests).toBe(maxRetries);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(numPollRequests === maxRetries);
expect(numPollRequests).toBe(maxRetries);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/davinci-suites/src/polling.test.ts` at line 83, The test's assertion
passes a boolean to expect causing it to always succeed; change the assertion to
use a Jest matcher on the actual value (e.g., replace the current
expect(numPollRequests === maxRetries) with an assertion like
expect(numPollRequests).toBe(maxRetries) or
expect(numPollRequests).toEqual(maxRetries)) so the test actually verifies that
numPollRequests equals maxRetries (refer to the variables numPollRequests and
maxRetries in polling.test.ts).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good catch.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cerebrl, glad it's useful! Worth noting that the same anti-pattern appears one more time earlier in the same test file at line 30:

expect(magicLink.startsWith('https://auth.pingone'));

This also passes a boolean to expect() and will always succeed. The fix would be:

expect(magicLink.startsWith('https://auth.pingone')).toBe(true);

or simply using a Playwright assertion:

expect(magicLink).toContain('https://auth.pingone');

(ミ ᵕ̣̣̣̣̣̣ ﻌ ᵕ̣̣̣̣̣̣ ミ)

});

test('should return expired status after challenge expires', async ({ page }) => {
const clientId = '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0';
const davinciPolicy = 'f40b544a4dfb575daa0cf5e9487c206a';
const { navigate } = asyncEvents(page);
await navigate(`/?clientId=${clientId}&acr_values=${davinciPolicy}`);

await expect(page.url()).toBe(
`http://localhost:5829/?clientId=${clientId}&acr_values=${davinciPolicy}`,
);

await page.getByRole('button', { name: 'Sign On' }).click();
await expect(page.getByRole('heading', { name: 'Polling' })).toBeVisible();

// Track poll retries
let numPollRequests = 0;
page.on('request', (request) => {
const method = request.method();
const requestUrl = request.url();

if (method === 'POST' && requestUrl.includes('/status')) {
numPollRequests++;
}
});

// Wait for challenge to expire
const challengeExpiry = 15000; // milliseconds
await page.waitForTimeout(challengeExpiry + 5000);

// Start polling
await page.getByRole('button', { name: 'Start polling' }).click();
await expect(page.getByText('Polling...')).toBeVisible();

// Check for expired status
await expect(page.getByText('Error: expired')).toBeVisible();

// Check poll count
expect(numPollRequests === 0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Incorrect assertion - test will always pass.

Same issue as Line 83: expect(numPollRequests === 0) evaluates the boolean expression first and passes it to expect(), which will always succeed.

🐛 Proposed fix
     // Check poll count
-    expect(numPollRequests === 0);
+    expect(numPollRequests).toBe(0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(numPollRequests === 0);
expect(numPollRequests).toBe(0);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/davinci-suites/src/polling.test.ts` at line 122, The assertion currently
passes a boolean expression into expect (expect(numPollRequests === 0)) which
evaluates before expect and thus the test always passes; update the assertion to
pass the value and use a matcher—replace the incorrect call with
expect(numPollRequests).toBe(0) (or another appropriate matcher) so the test
actually asserts that numPollRequests equals zero.

});
});

test.describe('Continue Polling', () => {
test('should succeed on QR code scan simulation', async ({ page }) => {
const clientId = '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0';
const davinciPolicy = '27aacf0efcc480dfcd00b04be8023cdc';
const { navigate } = asyncEvents(page);
await navigate(`/?clientId=${clientId}&acr_values=${davinciPolicy}`);

await expect(page.url()).toBe(
`http://localhost:5829/?clientId=${clientId}&acr_values=${davinciPolicy}`,
);

await expect(page.getByRole('heading', { name: 'Select Continue Polling Test' })).toBeVisible();
await page.getByRole('button', { name: 'Success' }).click();
await expect(page.getByRole('heading', { name: 'Polling' })).toBeVisible();

// Start polling
const numberCounterSuccess = 2;
for (let i = 0; i < numberCounterSuccess; i++) {
await page.getByRole('button', { name: 'Start polling' }).click();
await expect(page.getByText('Polling...')).toBeVisible();
await expect(page.getByRole('button', { name: 'Start polling' })).toBeDisabled();
}

// Check for success
await expect(page.getByText('Message: Done')).toBeVisible();
});

test('should timeout when retries are exhausted', async ({ page }) => {
const clientId = '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0';
const davinciPolicy = '27aacf0efcc480dfcd00b04be8023cdc';
const { navigate } = asyncEvents(page);
await navigate(`/?clientId=${clientId}&acr_values=${davinciPolicy}`);

await expect(page.url()).toBe(
`http://localhost:5829/?clientId=${clientId}&acr_values=${davinciPolicy}`,
);

await expect(page.getByRole('heading', { name: 'Select Continue Polling Test' })).toBeVisible();
await page.getByRole('button', { name: 'Timeout' }).click();
await expect(page.getByRole('heading', { name: 'Polling' })).toBeVisible();

// Start polling
const maxRetries = 3;
for (let i = 0; i < maxRetries + 1; i++) {
await page.getByRole('button', { name: 'Start polling' }).click();
await expect(page.getByText('Polling...')).toBeVisible();
await expect(page.getByRole('button', { name: 'Start polling' })).toBeDisabled();
}

// Check for timeout
await expect(page.getByText('Error: timedOut')).toBeVisible();
});
});
4 changes: 2 additions & 2 deletions e2e/davinci-suites/src/protect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test('Test Protect collector with Custom HTML component', async ({ page }) => {

await expect(page.getByText('JS Protect - Custom HTML Form')).toBeVisible();

const requests = [];
const requests: string[] = [];
page.on('request', (request) => {
const method = request.method();
const requestUrl = request.url();
Expand Down Expand Up @@ -54,7 +54,7 @@ test('Test Protect collector with P1 Forms component', async ({ page }) => {

await expect(page.getByText('Example - Sign On')).toBeVisible();

const requests = [];
const requests: string[] = [];
page.on('request', (request) => {
const method = request.method();
const requestUrl = request.url();
Expand Down
7 changes: 3 additions & 4 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ pre-commit:
nx-check:
run: pnpm nx affected -t typecheck lint build api-report --tui=false
stage_fixed: true
format:
run: pnpm nx format:write
stage_fixed: true
interface-mapping:
glob: >-
{tools/interface-mapping-validator/**/*.ts,
Expand All @@ -24,7 +21,9 @@ pre-commit:
echo "Interface mapping is out of sync." &&
echo "Run: pnpm mapping:generate" &&
exit 1)

format:
run: pnpm nx format:write
stage_fixed: true
commit-msg:
commands:
commitlint:
Expand Down
Loading