Skip to content

Commit

Permalink
Enable english leak detection suite on SIW Gen3 (#3596)
Browse files Browse the repository at this point in the history
OKTA-669879 Enable english leak detection suite
  • Loading branch information
denysoblohin-okta committed Apr 18, 2024
1 parent 91aafe9 commit b1b7ff5
Show file tree
Hide file tree
Showing 14 changed files with 160 additions and 43 deletions.
7 changes: 7 additions & 0 deletions .bacon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ test_suites:
script_name: detect-english-leaks
criteria: MERGE
queue_name: small
- name: detect-english-leaks-v3
script_path: /root/okta/okta-signin-widget/scripts
sort_order: '15'
timeout: '20'
script_name: detect-english-leaks-v3
criteria: MERGE
queue_name: small
- name: verify-registry-install
prereq_test_suite_name: publish
script_path: /root/okta/okta-signin-widget/scripts
Expand Down
24 changes: 20 additions & 4 deletions .testcaferc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { readFileSync } = require('fs');
const { RequestMock } = require('testcafe');
const waitOn = require('wait-on');

/**
* Escapes special regex chars in string so it can be used as the pattern for
Expand Down Expand Up @@ -50,6 +51,7 @@ const {
OKTA_SIW_ONLY_FLAKY,
OKTA_SIW_SKIP_FLAKY,
OKTA_SIW_GEN3,
OKTA_SIW_EN_LEAKS,
UPDATE_SCREENSHOTS,
} = process.env;

Expand All @@ -58,17 +60,31 @@ const env = {
OKTA_SIW_ONLY_FLAKY: OKTA_SIW_ONLY_FLAKY === 'true',
OKTA_SIW_SKIP_FLAKY: OKTA_SIW_SKIP_FLAKY === 'true',
OKTA_SIW_GEN3: OKTA_SIW_GEN3 === 'true',
OKTA_SIW_EN_LEAKS: OKTA_SIW_EN_LEAKS === 'true',
UPDATE_SCREENSHOTS: UPDATE_SCREENSHOTS === 'true',
};

const config = {
browsers: [ 'chrome:headless' ],
clientScripts: [
clientScripts: env.OKTA_SIW_EN_LEAKS ? [
] : [
{ module: 'axe-core/axe.min.js' },
{ module: '@testing-library/dom/dist/@testing-library/dom.umd.js' }
],
src: [ 'test/testcafe/spec/*_spec.js', 'test/testcafe/spec/v1/*_spec.js' ],
hooks: { request: mocks, },
src: env.OKTA_SIW_EN_LEAKS ? [
'test/testcafe/spec-en-leaks/*_spec.js',
] : [
'test/testcafe/spec/*_spec.js',
'test/testcafe/spec/v1/*_spec.js'
],
hooks: {
fixture: {
before: async () => {
await waitOn({ resources: ['http-get://localhost:3000'] });
}
},
request: mocks,
},
userVariables: {
gen3: env.OKTA_SIW_GEN3,
updateScreenshots: env.UPDATE_SCREENSHOTS,
Expand All @@ -82,7 +98,7 @@ const config = {
concurrency: OKTA_SIW_ONLY_FLAKY ? 1 : undefined,

// retry failed tests
quarantineMode: {
quarantineMode: env.OKTA_SIW_EN_LEAKS ? false : {
successThreshold: 1,
attemptLimit: 3,
},
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@
"test:testcafe-ci": "OKTA_SIW_SKIP_FLAKY=true run-p -r test:testcafe-setup test:testcafe-run -- 2>/dev/null",
"test:testcafe-ci-flaky": "OKTA_SIW_ONLY_FLAKY=true yarn test:testcafe-ci",
"test:testcafe-en-leaks-setup": "mkdir -p test-reports/testcafe && grunt exec:pseudo-loc && yarn start --open false",
"test:testcafe-run-en-leaks": "yarn test -t testcafe -c 2 chrome test/testcafe/spec-en-leaks/ --reporter spec",
"test:testcafe-run-en-leaks-ci": "yarn test -t testcafe -c 2 chrome:headless test/testcafe/spec-en-leaks/ --reporter xunit:build2/reports/junit/testcafe-results.xml",
"test:testcafe-run-en-leaks": "OKTA_SIW_EN_LEAKS=true yarn test -t testcafe -c 2 chrome --reporter spec",
"test:testcafe-run-en-leaks-ci": "OKTA_SIW_EN_LEAKS=true yarn test -t testcafe -c 2 chrome:headless --reporter xunit:build2/reports/junit/testcafe-results.xml",
"test:enleaks-ci": "run-p -r test:testcafe-en-leaks-setup test:testcafe-run-en-leaks-ci -- 2>/dev/null",
"test:enleaks": "run-p -r test:testcafe-en-leaks-setup test:testcafe-run-en-leaks -- 2>/dev/null",
"test:tsd": "yarn workspace @test/types test",
Expand All @@ -101,9 +101,12 @@
"copy:types": "grunt copy:types",
"build:types": "yarn clean:types && yarn generate:types && yarn copy:types",
"test:parity-setup": "mkdir -p test-reports/testcafe && yarn generate-config && ENTRY=debugger yarn build:webpack-release && yarn workspace v3 dev",
"test:parity-en-leaks-setup": "mkdir -p test-reports/testcafe && grunt assets && grunt exec:pseudo-loc && yarn workspace v3 dev",
"test:parity-run": "OKTA_SIW_GEN3=true yarn testcafe -c 4 chrome:headless --reporter spec,xunit:build2/reports/junit/testcafe-results.xml",
"test:parity-ci": "OKTA_SIW_SKIP_FLAKY=true yarn codegen && run-p -r test:parity-setup test:parity-run",
"test:parity-ci-flaky": "OKTA_SIW_GEN3=true OKTA_SIW_ONLY_FLAKY=true yarn test:parity-ci",
"test:parity-enleaks-ci": "yarn codegen && run-p -r test:parity-en-leaks-setup test:testcafe-run-en-leaks-ci -- 2>/dev/null",
"test:parity-enleaks": "run-p -r test:parity-en-leaks-setup test:testcafe-run-en-leaks -- 2>/dev/null",
"postinstall": "patch-package"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion playground/LocaleUtils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function assertNoEnglishLeaks(mockFile, viewText, noTranslationCont
Handle untranslated colon at the end of Odyssey screenreader text for field-level errors (reads as 'Error:')
TODO: Ask Odyssey why colon is not part of translation bundle
*/
extractedString = extractedString.replace('《:', '《');
extractedString = extractedString.replace(/《:/g, '《');
extractedString = extractedString.split(' ');
const enLeaks = extractedString.filter( item => {
return item.trim() && item.length && !pseudoLocSymbols.includes(item.trim());
Expand Down
24 changes: 21 additions & 3 deletions playground/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RenderResultSuccessNonOIDCSession,
} from '../src/types';
import { assertNoEnglishLeaks } from '../playground/LocaleUtils';
import Util from '../src/util/Util';

declare global {
const IE11_COMPAT_MODE: boolean;
Expand All @@ -32,7 +33,7 @@ declare global {
}
}

const NO_TRANSLATE_SELECTOR = '.no-translate, [translate="no"]';
const NO_TRANSLATE_SELECTOR = '.no-translate, .notranslate, [translate="no"]';

function isSuccessNonOIDC(res: RenderResult): res is RenderResultSuccessNonOIDCSession {
return (res as RenderResultSuccessNonOIDCSession).session !== undefined;
Expand All @@ -56,7 +57,7 @@ if (typeof window.OktaSignIn === 'undefined') {
// Make sure OktaSignIn is available
setTimeout(() => window.location.reload(), 2 * 1000);
}
const renderPlaygroundWidget = (options = {}) => {
const renderPlaygroundWidget = (options: WidgetOptions & { assertNoEnglishLeaks?: boolean } = {}) => {
// Okta-hosted widget page has this value set for CSP
window.cspNonce = 'playground';

Expand Down Expand Up @@ -125,7 +126,7 @@ const renderPlaygroundWidget = (options = {}) => {
console.log(JSON.stringify(context));

// assert english leaks if locale set to ok-PL
if (signinWidgetOptions.language === 'ok-PL') {
if (options?.assertNoEnglishLeaks !== false && signinWidgetOptions.language === 'ok-PL') {
//Use innerText to avoid including hidden elements
let viewText = document.getElementById('okta-sign-in').innerText;
viewText = viewText.split('\n').join(' ');
Expand Down Expand Up @@ -161,11 +162,28 @@ window.createWidgetInstance = createWidgetInstance;
window.renderPlaygroundWidget = renderPlaygroundWidget;

let render = true;
let preventRedirect = false;
if (typeof URL !== 'undefined') {
const searchParams = new URL(window.location.href).searchParams;
if (searchParams.get('render') === '0' || searchParams.get('render') === 'false') {
render = false;
}
if (searchParams.get('preventRedirect') === '1' || searchParams.get('preventRedirect') === 'true') {
preventRedirect = true;
}
}

if (preventRedirect) {
// Mocks that causes redirects:
// - error-with-failure-redirect
// - failure-redirect-remediation
// - identify-with-only-one-third-party-idp-app-user
// - identify-with-only-one-third-party-idp
// - success-redirect-remediation
// - success-with-app-user
// - success
Util.changeLocation = () => {};
Util.redirectWithFormGet = () => {};
}

let preRenderTasks = Promise.resolve();
Expand Down
17 changes: 17 additions & 0 deletions scripts/detect-english-leaks-v3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
export CHROME_HEADLESS=true
setup_service google-chrome-stable 119.0.6045.105-1

source $OKTA_HOME/$REPO/scripts/setup.sh

export TEST_SUITE_TYPE="junit"
export TEST_RESULT_FILE_DIR="${REPO}/build2/reports/junit"
echo $TEST_SUITE_TYPE > $TEST_SUITE_TYPE_FILE
echo $TEST_RESULT_FILE_DIR > $TEST_RESULT_FILE_DIR_FILE

if ! yarn test:parity-enleaks-ci; then
echo "testcafe tests failed! Exiting..."
exit ${PUBLISH_TYPE_AND_RESULT_DIR_BUT_ALWAYS_FAIL}
fi

exit ${PUBLISH_TYPE_AND_RESULT_DIR};
14 changes: 9 additions & 5 deletions src/util/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,15 +188,19 @@ Util.triggerAfterError = function(controller, err = {}) {
};

Util.redirect = function(url, win = window, isAppLink = false) {
if (!url) {
Logger.error(`Cannot redirect to empty URL: (${url})`);
return;
}
if (BrowserFeatures.isAndroid() && !isAppLink) {
Util.redirectWithFormGet(url);
} else {
win.location.href = url;
Util.changeLocation(url, win);
}
};

Util.changeLocation = function(url, win = window) {
if (!url) {
Logger.error(`Cannot redirect to empty URL: (${url})`);
return;
}
win.location.href = url;
};

Util.executeOnVisiblePage = function(cb) {
Expand Down
1 change: 1 addition & 0 deletions src/v2/view-builder/utils/AuthenticatorUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const getAuthenticatorData = function(authenticator, isVerifyAuthenticator) {
? authenticator.relatesTo?.profile?.email || ''
: loc('oie.email.authenticator.description', 'login'),
iconClassName: 'mfa-okta-email',
noTranslateClassName: isVerifyAuthenticator ? 'no-translate' : '',
buttonDataSeAttr: getButtonDataSeAttr(authenticator),
ariaLabel: isVerifyAuthenticator
? getVerifyEmailAriaLabel(authenticator.relatesTo?.profile?.email)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default View.extend({
<li>
{{i18n code="oie.enroll.okta_verify.setup.signInUrl" bundle="login"}}
<br><br/>
<span class='semi-strong'>{{sameDevice.orgUrl}}</span>
<span class='semi-strong no-translate'>{{sameDevice.orgUrl}}</span>
<a data-clipboard-text="{{sameDevice.orgUrl}}" class="button link-button copy-org-clipboard-button">
{{i18n code="enroll.oda.org.copyLink" bundle="login"}}
</a>
Expand Down
2 changes: 1 addition & 1 deletion src/v3/src/components/Redirect/Redirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Redirect: UISchemaElementComponent<{ uischema: RedirectElement }> = ({
// and when document is visible
if (options?.url) {
Util.executeOnVisiblePage(() => {
window.location.assign(options.url);
Util.changeLocation(options.url);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
10 changes: 9 additions & 1 deletion src/v3/src/util/languageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ export const loadLanguage = async (widgetProps: WidgetProps): Promise<void> => {
const languageCode = getLanguageCode(widgetProps);
const supportedLanguages = getSupportedLanguages(widgetProps);

// NOTE: If assets.baseUrl equals "/", SIW will incorrectly try to load language files
// from URL http://labels/json/login_xx.json
// Remove trailing slashes to match Gen2 behavior
let assetsBaseUrl = baseUrl ?? '';
if (assetsBaseUrl[assetsBaseUrl.length - 1] === '/') {
assetsBaseUrl = assetsBaseUrl.substring(0, assetsBaseUrl.length - 1);
}

return Bundles.loadLanguage(languageCode, i18n, {
baseUrl: baseUrl ?? '/',
baseUrl: assetsBaseUrl,
rewrite: rewrite ?? ((val) => val),
}, supportedLanguages);
};
Expand Down

0 comments on commit b1b7ff5

Please sign in to comment.