Skip to content

feat(pir): captcha providers #1550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 19, 2025
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { test as base } from '@playwright/test';
import { createConfiguredDbpTest } from './fixtures';
import { createGetRecaptchaInfoAction, createSolveRecaptchaAction } from '../mocks/broker-protection/captcha.js';
import { BROKER_PROTECTION_CONFIGS } from './tests-config.js';

const test = createConfiguredDbpTest(base);

test.describe('Broker Protection Captcha', () => {
test.describe('recaptcha2', () => {
const recaptchaTargetPage = 're-captcha.html';
const recaptchaResponseSelector = '#g-recaptcha-response';

test.describe('getCaptchaInfo', () => {
test.describe('with useEnhancedCaptchaSystem: "enabled"', () => {
test('returns the expected response for the correct action data', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction());
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});

test('returns the expected response for the correct action data without the "captchaType" field', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction({ captchaType: undefined }));
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});

test('returns the expected type when the "captchaType" field does not match the detected captcha type', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction({ captchaType: 'recaptchaEnterprise' }));
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});

test('returns an error response for an action data with an invalid "captchaType" field', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction({ captchaType: 'invalid' }));

await dbp.isCaptchaError();
});
});

test.describe('with useEnhancedCaptchaSystem: "disabled"', () => {
test('returns the expected response for the correct action data', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction());
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});

test('returns the expected response for the correct action data without the "captchaType" field', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction({ captchaType: undefined }));
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});

test('returns the expected type when the "captchaType" field does not match the detected captcha type', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction({ captchaType: 'recaptchaEnterprise' }));
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});

test('returns the expected response for an action data with an invalid "captchaType" field', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createGetRecaptchaInfoAction({ captchaType: 'invalid' }));
const sucessResponse = await dbp.getSuccessResponse();

dbp.isCaptchaMatch(sucessResponse, { captchaType: 'recaptcha2', targetPage: recaptchaTargetPage });
});
});
});

test.describe('solveCaptchaInfo', () => {
test.describe('with useEnhancedCaptchaSystem: "enabled"', () => {
test('solves the captcha for the correct action data', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction());
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});

test('solves the captcha for the correct action data without the "captchaType" field', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction({ captchaType: undefined }));
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});

test('solves the captcha for an action data when the "captchaType" field does not match the detected captcha type', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction({ captchaType: 'recaptchaEnterprise' }));
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});

test('returns an error response for an action data with an invalid "captchaType" field', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction({ captchaType: 'invalid' }));

await dbp.isCaptchaError();
});
});

test.describe('with useEnhancedCaptchaSystem: "disabled"', () => {
test('solves the captcha for the correct action data', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction());
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});

test('solves the captcha for the correct action data without the "captchaType" field', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction({ captchaType: undefined }));
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});

test('solves the captcha for an action when the "captchaType" field does not match the detected captcha type', async ({
createConfiguredDbp,
}) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction({ captchaType: 'recaptchaEnterprise' }));
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});

test('solves the captcha for an action data with an invalid "captchaType" field', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo(recaptchaTargetPage);
await dbp.receivesInlineAction(createSolveRecaptchaAction({ captchaType: 'invalid' }));
dbp.getSuccessResponse();

await dbp.isCaptchaTokenFilled(recaptchaResponseSelector);
});
});
});

test('remove query params from captcha url', async ({ createConfiguredDbp }) => {
const dbp = await createConfiguredDbp(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo('re-captcha.html?fname=john&lname=smith');
await dbp.receivesInlineAction(createGetRecaptchaInfoAction());
const sucessResponse = await dbp.getSuccessResponse();

dbp.isQueryParamRemoved(sucessResponse);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test, expect } from '@playwright/test';
import { BrokerProtectionPage } from './page-objects/broker-protection.js';
import { BrokerProtectionPage } from '../page-objects/broker-protection.js';
import { BROKER_PROTECTION_CONFIGS } from './tests-config.js';

test.describe('Broker Protection communications', () => {
test('sends an error when the action is not found', async ({ page }, workerInfo) => {
Expand Down Expand Up @@ -333,9 +334,19 @@ test.describe('Broker Protection communications', () => {
});
});
test.describe('Executes action and sends success message', () => {
test('buildUrl', async ({ page }, workerInfo) => {
test('buildUrl with useEnhancedCaptchaSystem: "enabled"', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.enabled();
await dbp.withFeatureConfig(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemEnabled);
await dbp.navigatesTo('results.html');
await dbp.receivesAction('navigate.json');
const response = await dbp.collector.waitForMessage('actionCompleted');
dbp.isSuccessMessage(response);
dbp.isUrlMatch(response[0].payload.params.result.success.response);
});

test('buildUrl with useEnhancedCaptchaSystem: "disabled"', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.withFeatureConfig(BROKER_PROTECTION_CONFIGS.enhancedCaptchaSystemDisabled);
await dbp.navigatesTo('results.html');
await dbp.receivesAction('navigate.json');
const response = await dbp.collector.waitForMessage('actionCompleted');
Expand Down Expand Up @@ -509,46 +520,6 @@ test.describe('Broker Protection communications', () => {
dbp.isSuccessMessage(response);
});

test('getCaptchaInfo', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.enabled();
await dbp.navigatesTo('captcha.html');
await dbp.receivesAction('get-captcha.json');
const response = await dbp.collector.waitForMessage('actionCompleted');
dbp.isSuccessMessage(response);
dbp.isCaptchaMatch(response[0].payload?.params.result.success.response);
});

test('getCaptchaInfo (hcaptcha)', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.enabled();
await dbp.navigatesTo('captcha2.html');
await dbp.receivesAction('get-captcha.json');
const response = await dbp.collector.waitForMessage('actionCompleted');
dbp.isSuccessMessage(response);
dbp.isHCaptchaMatch(response[0].payload?.params.result.success.response);
});

test('remove query params from captcha url', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.enabled();
await dbp.navigatesTo('captcha.html?fname=john&lname=smith');
await dbp.receivesAction('get-captcha.json');
const response = await dbp.collector.waitForMessage('actionCompleted');
dbp.isSuccessMessage(response);
dbp.isQueryParamRemoved(response[0].payload?.params.result.success.response);
});

test('solveCaptcha', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.enabled();
await dbp.navigatesTo('captcha.html');
await dbp.receivesAction('solve-captcha.json');
const response = await dbp.collector.waitForMessage('actionCompleted');
dbp.isSuccessMessage(response);
await dbp.isCaptchaTokenFilled();
});

test('expectation', async ({ page }, workerInfo) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.enabled();
Expand Down
56 changes: 56 additions & 0 deletions injected/integration-test/broker-protection-tests/fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { BrokerProtectionPage } from '../page-objects/broker-protection.js';
/**
* @import {PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType} from "@playwright/test"
*/

/**
* @param {typeof import("@playwright/test").test} test
* @return {TestType<PlaywrightTestArgs & PlaywrightTestOptions & PlaywrightWorkerArgs & PlaywrightWorkerOptions & { createConfiguredDbp: (config: Record<string, any>) => Promise<BrokerProtectionPage> }, {}>}
*/
export function createConfiguredDbpTest(test) {
return test.extend({
createConfiguredDbp: async ({ page }, use, workerInfo) => {
/**
* @param {Record<string, any>} config
*/
const createWithConfig = async (config) => {
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.withFeatureConfig(config);
return dbp;
};

await use(createWithConfig);
},
});
}

/**
* @param {typeof import("@playwright/test").test} test
* @return {TestType<PlaywrightTestArgs & PlaywrightTestOptions & PlaywrightWorkerArgs & PlaywrightWorkerOptions & { createConfiguredDbp: (config: Record<string, any>) => Promise<BrokerProtectionPage> }, {}>}
*/
export function createConfiguredDbpTestWithNavigation(test) {
return test.extend({
createConfiguredDbp: async ({ page }, use, workerInfo) => {
/**
* @param {object} params
* @param {string} params.targetPage
* @param {string|object} params.action
* @param {Record<string, any>} params.config
*/
const createWithConfig = async (params) => {
const { config, targetPage, action } = params;
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
await dbp.withFeatureConfig(config);
await dbp.navigatesTo(targetPage);
if (typeof action === 'string') {
dbp.receivesAction(action);
} else {
await dbp.receivesInlineAction(action);
}
return dbp;
};

await use(createWithConfig);
},
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createFeatureConfig } from '../mocks/broker-protection/feature-config';

export const BROKER_PROTECTION_CONFIGS = Object.freeze({
enhancedCaptchaSystemEnabled: createFeatureConfig({
state: 'enabled',
settings: {
useEnhancedCaptchaSystem: 'enabled',
},
}),
enhancedCaptchaSystemDisabled: createFeatureConfig({
state: 'enabled',
settings: {
useEnhancedCaptchaSystem: 'disabled',
},
}),
});
Loading
Loading