diff --git a/.changeset/config.json b/.changeset/config.json index 3ba4b52b1f..791171e604 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -24,8 +24,6 @@ "@forgerock/oidc-app", "@forgerock/oidc-suites", "@forgerock/local-release-tool", - "@forgerock/protect-app", - "@forgerock/protect-suites", "@forgerock/journey-app", "@forgerock/journey-suites", "@forgerock/recognize-app", diff --git a/AGENTS.md b/AGENTS.md index 0963453141..d11014db0b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,7 +93,6 @@ e2e/ ├── davinci-suites/ # Playwright e2e for DaVinci flows ├── journey-suites/ # Playwright e2e for Journey flows ├── oidc-suites/ # Playwright e2e for OIDC flows -├── protect-suites/ ├── am-mock-api/ # Mock AM server for journey e2e └── mock-api-v2/ # Mock API v2 ``` diff --git a/e2e/davinci-app/server-configs.ts b/e2e/davinci-app/server-configs.ts index 4eef1dfcc6..c41dda42a5 100644 --- a/e2e/davinci-app/server-configs.ts +++ b/e2e/davinci-app/server-configs.ts @@ -94,7 +94,7 @@ export const serverConfigs: Record = { }, }, /** - * AutoCollectors: Polling, Metadata, FIDO + * AutoCollectors: Polling, Metadata, FIDO, Protect */ '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0': { clientId: '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0', diff --git a/e2e/davinci-suites/src/protect.test.ts b/e2e/davinci-suites/src/protect.test.ts index 4830bb6b22..6345809a6f 100644 --- a/e2e/davinci-suites/src/protect.test.ts +++ b/e2e/davinci-suites/src/protect.test.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2025 - 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. @@ -8,76 +8,94 @@ import { expect, test } from '@playwright/test'; import { asyncEvents } from './utils/async-events.js'; import { username, password } from './utils/demo-user.js'; +const clientId = '31a587ce-9aa4-4f36-a09f-78cd8a0a74a0'; + test('Test Protect collector with Custom HTML component', async ({ page }) => { - const davinciFlow = 'ea02bcbfb2112e051c94ee9b08083d2d'; + const davinciFlow = '244e9bbec113931ae61fd962f0a1fe6c'; const { navigate } = asyncEvents(page); - await navigate(`/?acr_values=${davinciFlow}`); + await navigate(`/?clientId=${clientId}&acr_values=${davinciFlow}`); - await expect(page.url()).toBe(`http://localhost:5829/?acr_values=${davinciFlow}`); + await expect(page.url()).toBe( + `http://localhost:5829/?clientId=${clientId}&acr_values=${davinciFlow}`, + ); await expect(page.getByText('JS Protect - Custom HTML Form')).toBeVisible(); const requests: string[] = []; + let riskData; page.on('request', (request) => { const method = request.method(); const requestUrl = request.url(); const payload = request.postDataJSON(); - const data = payload.parameters.data.formData.riskSDK; requests.push(requestUrl); - if (method === 'POST' && requestUrl.includes('customHTMLTemplate')) { - expect(data).toBeDefined(); - expect(data).toMatch(/^R\/o\//); + // Only process POST requests with JSON payloads + if (method === 'POST' && payload && requestUrl.includes('customHTMLTemplate')) { + const data = payload.parameters?.data?.formData?.riskSDK; + if (data) { + riskData = data; + } } }); + const protectPromise = page.waitForRequest( + (req) => + req.method() === 'POST' && + req.url().includes('customHTMLTemplate') && + req.postDataJSON()?.parameters?.data?.formData?.riskSDK, + ); await page.getByLabel('Username').fill(username); await page.getByLabel('Password').fill(password); await page.getByRole('button', { name: 'Sign On' }).click(); + await protectPromise; - await expect( - page.getByText(/Sorry Bot, we cannot let you in this time.|You were blocked by PingOne Risk/), - ).toBeVisible(); - - const protectRequest = requests.some((url) => url.includes('customHTMLTemplate')); - await expect(protectRequest).toBeTruthy(); + expect(riskData).toBeDefined(); + expect(riskData).toMatch(/^R\/o\//); }); test('Test Protect collector with P1 Forms component', async ({ page }) => { - const davinciFlow = '908858ce3a809b579f11f49c4283b7a6'; + const davinciFlow = '99ccced66a6ad160b48d339c3d219d9c'; const { navigate } = asyncEvents(page); - await navigate(`/?acr_values=${davinciFlow}`); + await navigate(`/?clientId=${clientId}&acr_values=${davinciFlow}`); - await expect(page.url()).toBe(`http://localhost:5829/?acr_values=${davinciFlow}`); + await expect(page.url()).toBe( + `http://localhost:5829/?clientId=${clientId}&acr_values=${davinciFlow}`, + ); await expect(page.getByText('Example - Sign On')).toBeVisible(); const requests: string[] = []; + let riskData; page.on('request', (request) => { const method = request.method(); const requestUrl = request.url(); const payload = request.postDataJSON(); - const data = payload.parameters.data.formData.deviceRisk; requests.push(requestUrl); - if (method === 'POST' && requestUrl.includes('customForm')) { - expect(data).toBeDefined(); - expect(data).toMatch(/^R\/o\//); + // Only process POST requests with JSON payloads + if (method === 'POST' && payload && requestUrl.includes('customForm')) { + const data = payload.parameters?.data?.formData?.deviceRisk; + if (data) { + riskData = data; + } } }); + const protectPromise = page.waitForRequest( + (req) => + req.method() === 'POST' && + req.url().includes('customForm') && + req.postDataJSON()?.parameters?.data?.formData?.deviceRisk, + ); await page.getByLabel('Username').fill(username); await page.getByLabel('Password').fill(password); await page.getByRole('button', { name: 'Sign On' }).click(); + await protectPromise; - await expect( - page.getByText(/Sorry Bot, we cannot let you in this time.|You were blocked by PingOne Risk/), - ).toBeVisible(); - - const protectRequest = requests.some((url) => url.includes('customForm')); - await expect(protectRequest).toBeTruthy(); + expect(riskData).toBeDefined(); + expect(riskData).toMatch(/^R\/o\//); }); diff --git a/e2e/journey-suites/package.json b/e2e/journey-suites/package.json index dd4b6405e7..bbbcbdac3d 100644 --- a/e2e/journey-suites/package.json +++ b/e2e/journey-suites/package.json @@ -15,7 +15,14 @@ "author": "", "type": "module", "main": "src/index.js", + "dependencies": { + "@forgerock/journey-client": "workspace:*" + }, "nx": { - "implicitDependencies": ["@forgerock/journey-app", "@forgerock/mock-api-v2"] + "implicitDependencies": [ + "@forgerock/journey-app", + "@forgerock/mock-api-v2" + ], + "tags": ["scope:e2e"] } } diff --git a/e2e/journey-suites/src/protect.test.ts b/e2e/journey-suites/src/protect.test.ts index 09e37c4920..5879dc4c13 100644 --- a/e2e/journey-suites/src/protect.test.ts +++ b/e2e/journey-suites/src/protect.test.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2025 - 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. @@ -8,49 +8,58 @@ import { expect, test } from '@playwright/test'; import { asyncEvents } from './utils/async-events.js'; import { username, password } from './utils/demo-user.js'; +import type { Callback, NameValue } from '@forgerock/journey-client'; test('Test PingOne Protect journey flow', async ({ page }) => { const { clickButton } = asyncEvents(page); const messageArray: string[] = []; - let protectSignalsData: string | null = null; page.on('console', async (msg) => { messageArray.push(msg.text()); return Promise.resolve(true); }); + let riskData: string | null = null; + page.on('request', (request) => { - if (request.url().includes('/authenticate') && request.method() === 'POST') { - try { - const postData = request.postData(); - if (postData) { - const body = JSON.parse(postData); - const callbacks = body.callbacks || []; - for (const callback of callbacks) { - if (callback.type === 'PingOneProtectEvaluationCallback') { - const inputs = callback.input || []; - for (const input of inputs) { - if (input.name === 'IDToken1signals' && input.value) { - protectSignalsData = input.value; - } - } - } - } - } - } catch { - // Ignore parsing errors + const method = request.method(); + const requestUrl = request.url(); + const payload = request.postDataJSON(); + + // Only process POST requests with JSON payloads + if (method === 'POST' && payload && requestUrl.includes('/authenticate')) { + const callback: Callback = payload.callbacks?.find( + (callback: Callback) => callback.type === 'PingOneProtectEvaluationCallback', + ); + + if (callback) { + const data = callback.input?.find((input: NameValue) => input.name === 'IDToken1signals') + ?.value as string | undefined; + riskData = data ?? null; } } }); - await page.goto('/?journey=TEST_LoginPingProtect&clientId=basic', { waitUntil: 'load' }); + await page.goto('/?journey=TEST_LoginPingProtect&clientId=basic'); await expect(page.getByText('Initializing PingOne Protect...')).toBeVisible({ timeout: 10000 }); await expect(page.getByText('PingOne Protect initialized successfully!')).toBeVisible({ timeout: 15000, }); - await expect(page.getByLabel('User Name')).toBeVisible({ timeout: 15000 }); + const protectPromise = page.waitForRequest((req) => { + return ( + req.method() === 'POST' && + req.url().includes('/authenticate') && + req + .postDataJSON() + ?.callbacks?.some( + (callback: Callback) => callback.type === 'PingOneProtectEvaluationCallback', + ) + ); + }); + + await expect(page.getByLabel('User Name')).toBeVisible(); await page.getByLabel('User Name').fill(username); await page.getByLabel('Password').fill(password); await clickButton('Submit', '/authenticate'); @@ -60,24 +69,17 @@ test('Test PingOne Protect journey flow', async ({ page }) => { timeout: 15000, }); - // Wait for the evaluation callback to auto-submit and complete - await page.waitForResponse((response) => response.url().includes('/authenticate')); - - await expect(page.getByText('Complete')).toBeVisible({ timeout: 15000 }); + // Wait for risk data to be evaluated + await protectPromise; // Verify signals were captured from the request - expect(protectSignalsData).not.toBeNull(); - expect(typeof protectSignalsData).toBe('string'); - expect(protectSignalsData?.length).toBeGreaterThan(0); - - await clickButton('Logout', '/sessions'); - - await expect(page.getByText('Initializing PingOne Protect...')).toBeVisible({ timeout: 10000 }); + expect(riskData).not.toBeNull(); + expect(typeof riskData).toBe('string'); + expect(riskData).toMatch(/^R\/o\//); // Verify the protect SDK flow through console logs expect(messageArray.some((msg) => msg.includes('Protect initialized successfully'))).toBe(true); expect(messageArray.some((msg) => msg.includes('Protect data collected successfully'))).toBe( true, ); - expect(messageArray.some((msg) => msg.includes('Logout successful'))).toBe(true); }); diff --git a/e2e/journey-suites/tsconfig.json b/e2e/journey-suites/tsconfig.json index 08841a7f56..6a05bb606c 100644 --- a/e2e/journey-suites/tsconfig.json +++ b/e2e/journey-suites/tsconfig.json @@ -3,6 +3,9 @@ "files": [], "include": [], "references": [ + { + "path": "../../packages/journey-client" + }, { "path": "./tsconfig.e2e.json" } diff --git a/e2e/protect-app/.gitignore b/e2e/protect-app/.gitignore deleted file mode 100644 index a547bf36d8..0000000000 --- a/e2e/protect-app/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/e2e/protect-app/eslint.config.mjs b/e2e/protect-app/eslint.config.mjs deleted file mode 100644 index bdd9599d66..0000000000 --- a/e2e/protect-app/eslint.config.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import baseConfig from '../../eslint.config.mjs'; - -export default [ - { - ignores: [ - 'node_modules', - '*.md', - 'LICENSE', - '.babelrc', - '.env*', - '.bin', - 'dist', - '.eslintignore', - '**/*.html', - '*.svg', - '**/*.css', - 'public', - '*.json', - '*.d.ts', - '.gitignore', - 'tsconfig.tsbuildinfo', - ], - }, - ...baseConfig, - { - files: ['*.ts', '*.tsx', '*.js', '*.jsx'], - rules: {}, - }, - { - files: ['*.ts', '*.tsx'], - rules: {}, - }, - { - files: ['*.js', '*.jsx'], - rules: {}, - }, -]; diff --git a/e2e/protect-app/package.json b/e2e/protect-app/package.json deleted file mode 100644 index 46b1b90e2f..0000000000 --- a/e2e/protect-app/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "@forgerock/protect-app", - "version": "0.0.0", - "private": true, - "description": "Ping Protect Test Apps", - "type": "module", - "scripts": { - "build": "pnpm nx nxBuild", - "lint": "pnpm nx nxLint", - "preview": "pnpm nx nxPreview", - "serve": "pnpm nx nxServe" - }, - "dependencies": { - "@forgerock/javascript-sdk": "catalog:", - "@forgerock/protect": "workspace:*" - }, - "nx": { - "tags": ["scope:e2e"] - } -} diff --git a/e2e/protect-app/public/callback.html b/e2e/protect-app/public/callback.html deleted file mode 100644 index fda0cdf7a1..0000000000 --- a/e2e/protect-app/public/callback.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - Logged In - - -

Logged In

- - diff --git a/e2e/protect-app/public/typescript.svg b/e2e/protect-app/public/typescript.svg deleted file mode 100644 index d91c910cc3..0000000000 --- a/e2e/protect-app/public/typescript.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/e2e/protect-app/public/vite.svg b/e2e/protect-app/public/vite.svg deleted file mode 100644 index e7b8dfb1b2..0000000000 --- a/e2e/protect-app/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/e2e/protect-app/src/index.html b/e2e/protect-app/src/index.html deleted file mode 100644 index bd7d8e2443..0000000000 --- a/e2e/protect-app/src/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - Vite + TS - - -
- - - - - - -

Protect Home

-

Click on the Vite and TypeScript logos to learn more

- Home - Protect Native -
- - diff --git a/e2e/protect-app/src/protect-native.html b/e2e/protect-app/src/protect-native.html deleted file mode 100644 index 165384f452..0000000000 --- a/e2e/protect-app/src/protect-native.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - Ping Protect Native - - -
- - - - - - -

Ping Protect Native

-

Click on the Vite and TypeScript logos to learn more

- Home -
-
Loading...
-
- - - -
-
Protect initializing
-
Protect evaluating
-
-
-

Your user information:

-

-          
- -
-
-

Error code:

-

Start over

-
-
-

Something unexpected happened. Check the console for details.

-

Start over

-
-
-
- - - diff --git a/e2e/protect-app/src/protect-native.ts b/e2e/protect-app/src/protect-native.ts deleted file mode 100644 index 9e76487f03..0000000000 --- a/e2e/protect-app/src/protect-native.ts +++ /dev/null @@ -1,219 +0,0 @@ -/* - * - * Copyright © 2025 Ping Identity Corporation. All right reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - */ - -import './style.css'; -import { protect } from '@forgerock/protect'; -import type { Protect } from '@forgerock/protect/types'; -import { - CallbackType, - Config, - FRAuth, - FRStep, - FRUser, - NameCallback, - PasswordCallback, - PingOneProtectEvaluationCallback, - PingOneProtectInitializeCallback, - TokenManager, - UserManager, -} from '@forgerock/javascript-sdk'; - -const protectAPI: Protect = protect({ envId: '02fb4743-189a-4bc7-9d6c-a919edfe6447' }); -const FATAL = 'Fatal'; - -// Check URL for query parameters -const url = new URL(document.location.href); -const params = url.searchParams; -const goto = params.get('goto'); - -const logout = async () => { - try { - await FRUser.logout(); - location.reload(); - } catch (error) { - console.error(error); - } -}; - -// Show only the view for this handler -const showStep = (handler) => { - document.querySelectorAll('#steps > div').forEach((x) => x.classList.remove('active')); - const panel = document.getElementById(handler); - if (!panel) { - console.error(`No panel with ID "${handler}"" found`); - return false; - } - document.getElementById(handler)?.classList.add('active'); - return true; -}; - -const showUser = (user) => { - const userInfoEl = document.querySelector('#User pre'); - if (userInfoEl) { - userInfoEl.innerHTML = JSON.stringify(user, null, 2); - const panel = document.querySelector('#User'); - panel?.querySelector('.btn')?.addEventListener('click', () => { - logout(); - }); - showStep('User'); - } -}; - -// Get the next step using the FRAuth API -const nextStep = async (event?: Event, step?: FRStep) => { - event?.preventDefault(); - // eslint-disable-next-line @typescript-eslint/no-use-before-define - await FRAuth.next(step).then(handleStep).catch(handleFatalError); -}; - -// Define custom handlers to render and submit each expected step -const handlers = { - UsernamePassword: (step: FRStep) => { - const panel = document.querySelector('#UsernamePassword'); - panel?.querySelector('.btn')?.addEventListener('click', () => { - const nameCallback = step.getCallbackOfType(CallbackType.NameCallback); - const passwordCallback = step.getCallbackOfType( - CallbackType.PasswordCallback, - ); - nameCallback.setName( - (panel.querySelector('input[name=username]') as HTMLInputElement)?.value, - ); - passwordCallback.setPassword( - (panel.querySelector('input[type=password]') as HTMLInputElement)?.value, - ); - nextStep(event, step); - }); - }, - ProtectInit: async (step: FRStep) => { - const protectCallback = step.getCallbackOfType( - CallbackType.PingOneProtectInitializeCallback, - ); - const result = await protectAPI.start(); - console.log('protect initialized'); - - if (result?.error) { - console.error('error initailizing protect', result.error); - protectCallback.setClientError(result.error); - } - - nextStep(event, step); - }, - ProtectEval: async (step: FRStep) => { - console.log('protect evaluating'); - - const protectCallback = step.getCallbackOfType( - CallbackType.PingOneProtectEvaluationCallback, - ); - - const result = await protectAPI.getData(); - - if (typeof result !== 'string' && 'error' in result) { - console.error('error getting data', result.error); - protectCallback.setClientError(result.error); - } else { - console.log('received data'); - protectCallback.setData(result); - console.log('set data on evaluation callback'); - } - - nextStep(event, step); - }, - Error: (step) => { - const errorEl = document.querySelector('#Error span'); - if (errorEl) { - errorEl.innerHTML = step.getCode(); - } - }, - [FATAL]: (step) => { - console.log('fatal error', step); - }, -}; - -const getStage = (step) => { - // Check if the step contains callbacks for capturing username and password - const usernameCallbacks = step.getCallbacksOfType('NameCallback'); - const passwordCallbacks = step.getCallbacksOfType('PasswordCallback'); - const protectInitCallbacks = step.getCallbacksOfType('PingOneProtectInitializeCallback'); - const protectEvalCallbacks = step.getCallbacksOfType('PingOneProtectEvaluationCallback'); - - if (usernameCallbacks.length && passwordCallbacks.length) { - return 'UsernamePassword'; - } - if (protectInitCallbacks.length) { - return 'ProtectInit'; - } - if (protectEvalCallbacks.length) { - return 'ProtectEval'; - } - - return undefined; -}; - -// Display and bind the handler for this stage -const handleStep = async (step) => { - switch (step.type) { - case 'LoginSuccess': { - if (goto != null) { - window.location.replace(goto); - return; - } else { - // If we have a session token, get user information - const sessionToken = step.getSessionToken(); - const tokens = await TokenManager.getTokens(); - console.log(sessionToken, tokens); - const user = await UserManager.getCurrentUser(); - return showUser(user); - } - } - - case 'LoginFailure': { - showStep('Error'); - handlers.Error(step); - return; - } - - default: { - const stage = getStage(step) || FATAL; - if (!showStep(stage)) { - showStep(FATAL); - handlers[FATAL](step); - } else { - handlers[stage](step); - } - } - } -}; - -const handleFatalError = (err) => { - console.error('Fatal error', err); - showStep(FATAL); -}; - -// Begin the login flow -const startLoginFlow = async () => { - await Config.setAsync({ - clientId: 'WebOAuthClient', - redirectUri: `${window.location.origin}/callback.html`, - scope: 'openid profile email', - serverConfig: { - wellknown: - 'https://openam-sdks.forgeblocks.com/am/oauth2/alpha/.well-known/openid-configuration', - timeout: 3000, - }, - realmPath: 'alpha', - tree: 'TEST_Protect', - }); - await nextStep(); -}; - -document.getElementById('Error')?.addEventListener('click', nextStep); -document.getElementById('start-over')?.addEventListener('click', nextStep); -document.getElementById('Fatal')?.addEventListener('click', nextStep); - -await startLoginFlow(); diff --git a/e2e/protect-app/src/style.css b/e2e/protect-app/src/style.css deleted file mode 100644 index d679697502..0000000000 --- a/e2e/protect-app/src/style.css +++ /dev/null @@ -1,111 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.vanilla:hover { - filter: drop-shadow(0 0 2em #3178c6aa); -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} - -.nav { - display: block; -} - -#steps { - position: relative; -} -#steps > div { - left: -20000px; - position: absolute; -} -#steps > div.active { - left: 0; -} diff --git a/e2e/protect-app/tsconfig.app.json b/e2e/protect-app/tsconfig.app.json deleted file mode 100644 index 4a099ebc5d..0000000000 --- a/e2e/protect-app/tsconfig.app.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "moduleResolution": "Bundler" - }, - "exclude": ["**/*.spec.ts", "**/*.test.ts"], - "include": [ - "./main.ts", - "./helper.ts", - "./server-configs.ts", - "components/**/*.ts" - ], - "references": [ - { - "path": "../../packages/protect/tsconfig.lib.json" - } - ] -} diff --git a/e2e/protect-app/tsconfig.json b/e2e/protect-app/tsconfig.json deleted file mode 100644 index 9362c61427..0000000000 --- a/e2e/protect-app/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "compilerOptions": { - "moduleResolution": "Bundler", - "module": "ES2020", - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["es2020", "dom", "dom.iterable"], - "strict": true, - "isolatedModules": true, - "esModuleInterop": true, - "noUnusedLocals": true, - "noUnusedParameters": false, - "noImplicitReturns": true, - "skipLibCheck": true - }, - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] -} diff --git a/e2e/protect-app/tsconfig.spec.json b/e2e/protect-app/tsconfig.spec.json deleted file mode 100644 index 5fa5138652..0000000000 --- a/e2e/protect-app/tsconfig.spec.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "ES2020", - "moduleResolution": "Bundler", - "outDir": "../../dist/out-tsc", - "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] - }, - "include": [ - "vite.config.ts", - "src/**/*.test.ts", - "src/**/*.spec.ts", - "src/**/*.test.tsx", - "src/**/*.spec.tsx", - "src/**/*.test.js", - "src/**/*.spec.js", - "src/**/*.test.jsx", - "src/**/*.spec.jsx", - "src/**/*.d.ts", - "vite-env.d.ts" - ] -} diff --git a/e2e/protect-app/vite-env.d.ts b/e2e/protect-app/vite-env.d.ts deleted file mode 100644 index 11f02fe2a0..0000000000 --- a/e2e/protect-app/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/e2e/protect-app/vite.config.ts b/e2e/protect-app/vite.config.ts deleted file mode 100644 index c1402915ec..0000000000 --- a/e2e/protect-app/vite.config.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as path from 'path'; -import { defineConfig } from 'vite'; - -export default defineConfig({ - root: __dirname + '/src', - publicDir: __dirname + '/public', - build: { - outDir: __dirname + '/dist', - emptyOutDir: true, - reportCompressedSize: true, - target: 'esnext', - minify: false, - rollupOptions: { - input: { - main: path.resolve(__dirname + '/src', 'index.html'), - protectNative: path.resolve(__dirname + '/src', 'protect-native.html'), - }, - output: { - entryFileNames: '[name].js', - }, - }, - }, - preview: { - port: 8443, - }, - server: { - port: 8443, - headers: { - 'Service-Worker-Allowed': '/', - 'Service-Worker': 'script', - }, - strictPort: true, - }, -}); diff --git a/e2e/protect-suites/eslint.config.mjs b/e2e/protect-suites/eslint.config.mjs deleted file mode 100644 index 80142e0863..0000000000 --- a/e2e/protect-suites/eslint.config.mjs +++ /dev/null @@ -1,28 +0,0 @@ -import baseConfig from '../../eslint.config.mjs'; -export default [ - { - ignores: [ - '.playwright/', - 'node_modules', - '*.md', - 'LICENSE', - '.babelrc', - '.env*', - '.bin', - 'dist', - ], - }, - ...baseConfig, - { - files: ['*.ts', '*.tsx', '*.js', '*.jsx'], - rules: {}, - }, - { - files: ['*.ts', '*.tsx'], - rules: {}, - }, - { - files: ['*.js', '*.jsx'], - rules: {}, - }, -]; diff --git a/e2e/protect-suites/package.json b/e2e/protect-suites/package.json deleted file mode 100644 index 3cf09710c8..0000000000 --- a/e2e/protect-suites/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@forgerock/protect-suites", - "version": "0.0.0", - "private": true, - "description": "Ping Protect E2E Suites", - "homepage": "https://github.com/ForgeRock/ping-javascript-sdk#readme", - "bugs": { - "url": "https://github.com/ForgeRock/ping-javascript-sdk/issues" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/ForgeRock/ping-javascript-sdk.git" - }, - "license": "ISC", - "author": "", - "type": "module", - "main": "src/index.js", - "nx": { - "implicitDependencies": ["@forgerock/protect-app"] - } -} diff --git a/e2e/protect-suites/playwright.config.ts b/e2e/protect-suites/playwright.config.ts deleted file mode 100644 index 01d088798c..0000000000 --- a/e2e/protect-suites/playwright.config.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { PlaywrightTestConfig } from '@playwright/test'; -import { workspaceRoot } from '@nx/devkit'; - -// For CI, you may want to set BASE_URL to the deployed application. -const baseURL = process.env['BASE_URL'] || 'http://localhost:8443'; - -const config: PlaywrightTestConfig = { - outputDir: './.playwright', - testDir: './src', - reporter: process.env.CI ? 'github' : 'list', - timeout: 30000, - use: { - baseURL, - headless: true, - ignoreHTTPSErrors: true, - geolocation: { latitude: 24.9884, longitude: -87.3459 }, - bypassCSP: true, - trace: process.env.CI ? 'on-first-retry' : 'retain-on-failure', - }, - webServer: [ - { - command: 'pnpm nx serve @forgerock/protect-app', - port: 8443, - ignoreHTTPSErrors: true, - reuseExistingServer: !process.env.CI, - cwd: workspaceRoot, - }, - ], -}; - -export default config; diff --git a/e2e/protect-suites/src/protect-native.test.ts b/e2e/protect-suites/src/protect-native.test.ts deleted file mode 100644 index 077117092b..0000000000 --- a/e2e/protect-suites/src/protect-native.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * - * Copyright © 2025 Ping Identity Corporation. All right 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 { password, username } from './utils/demo-user.js'; - -test.describe('Test basic login flow with Ping Protect', () => { - test.afterEach(({ page }) => { - page.removeListener('console', (msg) => console.log(msg.text())); - }); - - test('should send Protect data and login successfully', async ({ page }) => { - const logs = []; - page.on('console', async (msg) => { - logs.push(msg.text()); - return Promise.resolve(true); - }); - - await page.goto('/protect-native.html'); - await expect(page.url()).toBe('http://localhost:8443/protect-native.html'); - - await expect(page.getByText('Ping Protect Native')).toBeVisible(); - await expect(page.getByText('Protect initializing')).toBeVisible(); - - await page.getByPlaceholder('Username').fill(username); - await page.getByPlaceholder('Password').fill(password); - await page.getByRole('button', { name: 'Sign In' }).click(); - - await expect(page.getByText('Protect evaluating')).toBeVisible(); - - await page.waitForRequest('https://openam-sdks.forgeblocks.com/am/oauth2/alpha/userinfo'); - await expect(page.getByText('Your user information:')).toBeVisible(); - await expect(page.getByText('sdkuser@example.com')).toBeVisible(); - - await expect(logs.includes('protect initialized')).toBeTruthy(); - await expect(logs.includes('protect evaluating')).toBeTruthy(); - await expect(logs.includes('received data')).toBeTruthy(); - await expect(logs.includes('set data on evaluation callback')).toBeTruthy(); - }); -}); diff --git a/e2e/protect-suites/src/utils/demo-user.ts b/e2e/protect-suites/src/utils/demo-user.ts deleted file mode 100644 index adb40b1401..0000000000 --- a/e2e/protect-suites/src/utils/demo-user.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * - * Copyright © 2025 Ping Identity Corporation. All right reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - * - */ - -export const username = 'sdkuser'; -export const password = 'password'; diff --git a/e2e/protect-suites/tsconfig.e2e.json b/e2e/protect-suites/tsconfig.e2e.json deleted file mode 100644 index 8852c8082f..0000000000 --- a/e2e/protect-suites/tsconfig.e2e.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "sourceMap": false, - "outDir": "../../dist/out-tsc", - "allowJs": true, - "module": "ES2020", - "moduleResolution": "bundler" - }, - "include": ["**/*.ts", "**/*.js"] -} diff --git a/e2e/protect-suites/tsconfig.json b/e2e/protect-suites/tsconfig.json deleted file mode 100644 index 08841a7f56..0000000000 --- a/e2e/protect-suites/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.e2e.json" - } - ] -} diff --git a/packages/davinci-client/api-report/davinci-client.api.md b/packages/davinci-client/api-report/davinci-client.api.md index 831dc62040..2dc2071de1 100644 --- a/packages/davinci-client/api-report/davinci-client.api.md +++ b/packages/davinci-client/api-report/davinci-client.api.md @@ -288,26 +288,26 @@ export function davinci(input: { resume: (input: { continueToken: string; }) => Promise; - start: (options?: StartOptions | undefined) => Promise; + start: (options?: StartOptions | undefined) => Promise; update: (collector: T) => Updater; validate: (collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors | AutoCollectors) => Validator; pollStatus: (collector: PollingCollector) => Poller; getClient: () => { - status: "start"; - } | { action: string; collectors: Collectors[]; description?: string; name?: string; - status: "error"; - } | { - status: "failure"; + status: "continue"; } | { action: string; collectors: Collectors[]; description?: string; name?: string; - status: "continue"; + status: "error"; + } | { + status: "failure"; + } | { + status: "start"; } | { authorization?: { code?: string; @@ -318,9 +318,15 @@ export function davinci(input: { getCollectors: () => Collectors[]; getError: () => DaVinciError | null; getErrorCollectors: () => CollectorErrors[]; - getNode: () => StartNode | ErrorNode | FailureNode | ContinueNode | SuccessNode; + getNode: () => ContinueNode | ErrorNode | FailureNode | StartNode | SuccessNode; getServer: () => { - status: "start"; + _links?: Links; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + eventName?: string; + status: "continue"; } | { _links?: Links; eventName?: string; @@ -337,13 +343,7 @@ export function davinci(input: { interactionToken?: string; status: "failure"; } | { - _links?: Links; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - eventName?: string; - status: "continue"; + status: "start"; } | { _links?: Links; eventName?: string; diff --git a/packages/davinci-client/api-report/davinci-client.types.api.md b/packages/davinci-client/api-report/davinci-client.types.api.md index 5842a5764c..d498f3a14f 100644 --- a/packages/davinci-client/api-report/davinci-client.types.api.md +++ b/packages/davinci-client/api-report/davinci-client.types.api.md @@ -288,26 +288,26 @@ export function davinci(input: { resume: (input: { continueToken: string; }) => Promise; - start: (options?: StartOptions | undefined) => Promise; + start: (options?: StartOptions | undefined) => Promise; update: (collector: T) => Updater; validate: (collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors | AutoCollectors) => Validator; pollStatus: (collector: PollingCollector) => Poller; getClient: () => { - status: "start"; - } | { action: string; collectors: Collectors[]; description?: string; name?: string; - status: "error"; - } | { - status: "failure"; + status: "continue"; } | { action: string; collectors: Collectors[]; description?: string; name?: string; - status: "continue"; + status: "error"; + } | { + status: "failure"; + } | { + status: "start"; } | { authorization?: { code?: string; @@ -318,9 +318,15 @@ export function davinci(input: { getCollectors: () => Collectors[]; getError: () => DaVinciError | null; getErrorCollectors: () => CollectorErrors[]; - getNode: () => StartNode | ErrorNode | FailureNode | ContinueNode | SuccessNode; + getNode: () => ContinueNode | ErrorNode | FailureNode | StartNode | SuccessNode; getServer: () => { - status: "start"; + _links?: Links; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + eventName?: string; + status: "continue"; } | { _links?: Links; eventName?: string; @@ -337,13 +343,7 @@ export function davinci(input: { interactionToken?: string; status: "failure"; } | { - _links?: Links; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - eventName?: string; - status: "continue"; + status: "start"; } | { _links?: Links; eventName?: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d893b5305..869e7d3f15 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -374,7 +374,11 @@ importers: specifier: workspace:* version: link:../../packages/sdk-effects/logger - e2e/journey-suites: {} + e2e/journey-suites: + dependencies: + '@forgerock/journey-client': + specifier: workspace:* + version: link:../../packages/journey-client e2e/mock-api-v2: dependencies: @@ -427,17 +431,6 @@ importers: e2e/oidc-suites: {} - e2e/protect-app: - dependencies: - '@forgerock/javascript-sdk': - specifier: 'catalog:' - version: 4.9.0 - '@forgerock/protect': - specifier: workspace:* - version: link:../../packages/protect - - e2e/protect-suites: {} - e2e/recognize-app: dependencies: '@forgerock/journey-client': diff --git a/tsconfig.json b/tsconfig.json index d29f6efc7f..5bac2012e8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,12 +22,6 @@ { "path": "./e2e/mock-api-v2" }, - { - "path": "./e2e/protect-app" - }, - { - "path": "./e2e/protect-suites" - }, { "path": "./e2e/device-client-app" },