Files
FlowPilot/tests/auth-japanese-localization.test.js
T
2026-05-21 07:21:34 +08:00

330 lines
9.9 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('flows/openai/content/openai-auth.js', 'utf8');
function extractFunction(name) {
const markers = [`async function ${name}(`, `function ${name}(`];
const start = markers
.map((marker) => source.indexOf(marker))
.find((index) => index >= 0);
if (start < 0) {
throw new Error(`missing function ${name}`);
}
let parenDepth = 0;
let signatureEnded = false;
let braceStart = -1;
for (let i = start; i < source.length; i += 1) {
const ch = source[i];
if (ch === '(') {
parenDepth += 1;
} else if (ch === ')') {
parenDepth -= 1;
if (parenDepth === 0) {
signatureEnded = true;
}
} else if (ch === '{' && signatureEnded) {
braceStart = i;
break;
}
}
if (braceStart < 0) {
throw new Error(`missing body for function ${name}`);
}
let depth = 0;
let end = braceStart;
for (; end < source.length; end += 1) {
const ch = source[end];
if (ch === '{') depth += 1;
if (ch === '}') {
depth -= 1;
if (depth === 0) {
end += 1;
break;
}
}
}
return source.slice(start, end);
}
function extractConst(name) {
const pattern = new RegExp(`const\\s+${name}\\s*=\\s*[\\s\\S]*?;`);
const match = source.match(pattern);
if (!match) {
throw new Error(`missing const ${name}`);
}
return match[0];
}
test('login step recognizes Japanese email and phone entry actions', () => {
const api = new Function(`
const emailButton = {
textContent: 'メールアドレスで続行',
disabled: false,
getAttribute(name) {
if (name === 'aria-disabled') return 'false';
return '';
},
};
const phoneButton = {
textContent: '電話番号で続行',
disabled: false,
getAttribute(name) {
if (name === 'aria-disabled') return 'false';
return '';
},
};
const codeButton = {
textContent: 'コードでサインイン',
disabled: false,
getAttribute(name) {
if (name === 'aria-disabled') return 'false';
return '';
},
};
const document = {
querySelectorAll(selector) {
if (selector === 'button, a, [role="button"], [role="link"], input[type="button"], input[type="submit"]') {
return [codeButton, phoneButton, emailButton];
}
return [];
},
};
${extractConst('ONE_TIME_CODE_LOGIN_PATTERN')}
${extractConst('LOGIN_ENTRY_ACTION_PATTERN')}
${extractConst('LOGIN_SWITCH_TO_PHONE_PATTERN')}
${extractConst('LOGIN_PHONE_ACTION_PATTERN')}
${extractConst('LOGIN_EXTERNAL_IDP_PATTERN')}
${extractConst('LOGIN_CODE_ONLY_ACTION_PATTERN')}
function isVisibleElement(el) { return Boolean(el); }
function isActionEnabled(el) { return Boolean(el) && !el.disabled && el.getAttribute('aria-disabled') !== 'true'; }
function getActionText(el) { return [el?.textContent, el?.value, el?.getAttribute?.('aria-label'), el?.getAttribute?.('title')].filter(Boolean).join(' ').replace(/\\s+/g, ' ').trim(); }
${extractFunction('findLoginEntryTrigger')}
${extractFunction('findLoginPhoneEntryTrigger')}
${extractFunction('findOneTimeCodeLoginTrigger')}
return {
email() { return getActionText(findLoginEntryTrigger()); },
phone() { return getActionText(findLoginPhoneEntryTrigger()); },
code() { return getActionText(findOneTimeCodeLoginTrigger()); },
};
`)();
assert.equal(api.email(), 'メールアドレスで続行');
assert.equal(api.phone(), '電話番号で続行');
assert.equal(api.code(), 'コードでサインイン');
});
test('login submit button recognizes Japanese login and continue labels', () => {
const api = new Function(`
const loginButton = {
textContent: 'ログイン',
disabled: false,
getAttribute(name) {
if (name === 'type') return 'button';
if (name === 'aria-disabled') return 'false';
return '';
},
};
const document = {
querySelector() { return null; },
querySelectorAll(selector) {
if (selector === 'button, a, [role="button"], [role="link"], input[type="button"], input[type="submit"]') {
return [loginButton];
}
return [];
},
};
${extractConst('ONE_TIME_CODE_LOGIN_PATTERN')}
function isVisibleElement(el) { return Boolean(el); }
function isActionEnabled(el) { return Boolean(el) && !el.disabled && el.getAttribute('aria-disabled') !== 'true'; }
function getActionText(el) { return [el?.textContent, el?.value, el?.getAttribute?.('aria-label'), el?.getAttribute?.('title')].filter(Boolean).join(' ').replace(/\\s+/g, ' ').trim(); }
${extractFunction('getLoginSubmitButton')}
return {
run() { return getActionText(getLoginSubmitButton()); },
};
`)();
assert.equal(api.run(), 'ログイン');
});
test('verification helpers recognize Japanese resend and submit actions', () => {
const api = new Function(`
const resendButton = {
textContent: 'コードを再送信',
disabled: false,
getAttribute(name) {
if (name === 'aria-disabled') return 'false';
return '';
},
};
const verifyButton = {
textContent: '確認',
disabled: false,
getAttribute(name) {
if (name === 'aria-disabled') return 'false';
return '';
},
};
const codeInput = {
form: {
querySelectorAll(selector) {
if (selector === 'button[type="submit"], input[type="submit"]') return [];
if (selector === 'button, [role="button"], input[type="button"], input[type="submit"]') return [verifyButton];
return [];
},
},
closest() { return null; },
};
const document = {
querySelectorAll(selector) {
if (selector === 'button, a, [role="button"], [role="link"], input[type="button"], input[type="submit"]') {
return [resendButton];
}
return [];
},
};
${extractConst('RESEND_VERIFICATION_CODE_PATTERN')}
function isVisibleElement(el) { return Boolean(el); }
function isActionEnabled(el) { return Boolean(el) && !el.disabled && el.getAttribute('aria-disabled') !== 'true'; }
function getActionText(el) { return [el?.textContent, el?.value, el?.getAttribute?.('aria-label'), el?.getAttribute?.('title')].filter(Boolean).join(' ').replace(/\\s+/g, ' ').trim(); }
${extractFunction('findResendVerificationCodeTrigger')}
${extractFunction('getVerificationSubmitButtonForTarget')}
return {
resend() { return getActionText(findResendVerificationCodeTrigger()); },
submit() { return getActionText(getVerificationSubmitButtonForTarget(codeInput)); },
};
`)();
assert.equal(api.resend(), 'コードを再送信');
assert.equal(api.submit(), '確認');
});
test('OAuth consent detection recognizes Japanese consent copy and continue button', () => {
const api = new Function(`
const continueButton = {
textContent: '続行',
disabled: false,
getAttribute(name) {
if (name === 'data-dd-action-name') return '';
if (name === 'aria-disabled') return 'false';
return '';
},
};
const consentForm = {
querySelectorAll(selector) {
if (selector === 'button[type="submit"], input[type="submit"], [role="button"]') {
return [continueButton];
}
return [];
},
};
const document = {
body: {
innerText: 'ChatGPTを使用してCodexにサインイン',
textContent: 'ChatGPTを使用してCodexにサインイン',
},
querySelector(selector) {
if (selector === OAUTH_CONSENT_FORM_SELECTOR) return consentForm;
return null;
},
querySelectorAll(selector) {
if (selector === 'button, [role="button"]') return [continueButton];
return [];
},
};
${extractConst('OAUTH_CONSENT_PAGE_PATTERN')}
${extractConst('OAUTH_CONSENT_FORM_SELECTOR')}
${extractConst('CONTINUE_ACTION_PATTERN')}
function isVisibleElement(el) { return Boolean(el); }
function getActionText(el) { return [el?.textContent, el?.value, el?.getAttribute?.('aria-label'), el?.getAttribute?.('title')].filter(Boolean).join(' ').replace(/\\s+/g, ' ').trim(); }
${extractFunction('getPageTextSnapshot')}
${extractFunction('getOAuthConsentForm')}
${extractFunction('getPrimaryContinueButton')}
${extractFunction('isOAuthConsentPage')}
return {
consent() { return isOAuthConsentPage(); },
button() { return getActionText(getPrimaryContinueButton()); },
};
`)();
assert.equal(api.consent(), true);
assert.equal(api.button(), '続行');
});
test('profile step recognizes Japanese account creation and day label', () => {
const api = new Function(`
const submitButton = {
textContent: 'アカウントを作成',
getAttribute(name) {
if (name === 'type') return 'button';
return '';
},
};
const dayLabel = { textContent: '日' };
const dayRoot = {
querySelectorAll(selector) {
if (selector === 'span') return [dayLabel];
return [];
},
querySelector(selector) {
if (selector === 'button[aria-haspopup="listbox"]') return { id: 'day-button' };
if (selector === '.react-aria-SelectValue') return { textContent: '' };
return null;
},
closest() { return null; },
parentElement: {
querySelector(selector) {
if (selector === '[data-testid="hidden-select-container"] select') return { id: 'day-select' };
return null;
},
},
};
const document = {
querySelector(selector) {
if (selector === 'button[type="submit"], input[type="submit"]') return null;
return null;
},
querySelectorAll(selector) {
if (selector === 'button, [role="button"], input[type="button"], input[type="submit"]') return [submitButton];
if (selector === '.react-aria-Select') return [dayRoot];
return [];
},
};
function isVisibleElement(el) { return Boolean(el); }
function getActionText(el) { return [el?.textContent, el?.value, el?.getAttribute?.('aria-label'), el?.getAttribute?.('title')].filter(Boolean).join(' ').replace(/\\s+/g, ' ').trim(); }
${extractFunction('normalizeInlineText')}
${extractFunction('getStep5SubmitButton')}
${extractFunction('findBirthdayReactAriaSelect')}
return {
submit() { return getActionText(getStep5SubmitButton()); },
day() { return findBirthdayReactAriaSelect('日')?.nativeSelect?.id || ''; },
};
`)();
assert.equal(api.submit(), 'アカウントを作成');
assert.equal(api.day(), 'day-select');
});