183 lines
5.0 KiB
JavaScript
183 lines
5.0 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
const source = fs.readFileSync('sidepanel/sidepanel.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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
test('new user guide prompt is only eligible before the one-time dismissal is set', () => {
|
|
const bundle = [
|
|
extractFunction('isPromptDismissed'),
|
|
extractFunction('setPromptDismissed'),
|
|
extractFunction('isNewUserGuidePromptDismissed'),
|
|
extractFunction('setNewUserGuidePromptDismissed'),
|
|
extractFunction('shouldPromptNewUserGuide'),
|
|
].join('\n');
|
|
|
|
const api = new Function(`
|
|
const NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-new-user-guide-prompt-dismissed';
|
|
const storage = new Map();
|
|
const localStorage = {
|
|
getItem(key) {
|
|
return storage.has(key) ? storage.get(key) : null;
|
|
},
|
|
setItem(key, value) {
|
|
storage.set(key, String(value));
|
|
},
|
|
removeItem(key) {
|
|
storage.delete(key);
|
|
},
|
|
};
|
|
const btnContributionMode = { disabled: false };
|
|
let latestState = { contributionMode: false };
|
|
${bundle}
|
|
return {
|
|
shouldPromptNewUserGuide,
|
|
setDismissed(value) {
|
|
setNewUserGuidePromptDismissed(value);
|
|
},
|
|
setButtonDisabled(value) {
|
|
btnContributionMode.disabled = Boolean(value);
|
|
},
|
|
setContributionMode(value) {
|
|
latestState = { contributionMode: Boolean(value) };
|
|
},
|
|
};
|
|
`)();
|
|
|
|
assert.equal(api.shouldPromptNewUserGuide(), true);
|
|
|
|
api.setDismissed(true);
|
|
assert.equal(api.shouldPromptNewUserGuide(), false);
|
|
|
|
api.setDismissed(false);
|
|
api.setButtonDisabled(true);
|
|
assert.equal(api.shouldPromptNewUserGuide(), false);
|
|
|
|
api.setButtonDisabled(false);
|
|
api.setContributionMode(true);
|
|
assert.equal(api.shouldPromptNewUserGuide(), false);
|
|
});
|
|
|
|
test('new user guide prompt persists dismissal before awaiting the user choice and opens the contribution page on confirm', async () => {
|
|
const bundle = [
|
|
extractFunction('isPromptDismissed'),
|
|
extractFunction('setPromptDismissed'),
|
|
extractFunction('isNewUserGuidePromptDismissed'),
|
|
extractFunction('setNewUserGuidePromptDismissed'),
|
|
extractFunction('shouldPromptNewUserGuide'),
|
|
extractFunction('getContributionPortalUrl'),
|
|
extractFunction('openNewUserGuidePrompt'),
|
|
extractFunction('maybeShowNewUserGuidePrompt'),
|
|
].join('\n');
|
|
|
|
const api = new Function(`
|
|
const NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-new-user-guide-prompt-dismissed';
|
|
const storage = new Map();
|
|
const localStorage = {
|
|
getItem(key) {
|
|
return storage.has(key) ? storage.get(key) : null;
|
|
},
|
|
setItem(key, value) {
|
|
storage.set(key, String(value));
|
|
},
|
|
removeItem(key) {
|
|
storage.delete(key);
|
|
},
|
|
};
|
|
const btnContributionMode = { disabled: false };
|
|
const latestState = { contributionMode: false };
|
|
const contributionContentService = { portalUrl: 'https://apikey.qzz.io' };
|
|
const openedUrls = [];
|
|
let modalOptions = null;
|
|
let nextChoice = 'confirm';
|
|
function openExternalUrl(url) {
|
|
openedUrls.push(url);
|
|
}
|
|
function openActionModal(options) {
|
|
modalOptions = options;
|
|
return Promise.resolve(nextChoice);
|
|
}
|
|
${bundle}
|
|
return {
|
|
maybeShowNewUserGuidePrompt,
|
|
getDismissed() {
|
|
return localStorage.getItem(NEW_USER_GUIDE_PROMPT_DISMISSED_STORAGE_KEY);
|
|
},
|
|
getOpenedUrls() {
|
|
return openedUrls.slice();
|
|
},
|
|
getModalOptions() {
|
|
return modalOptions;
|
|
},
|
|
setNextChoice(choice) {
|
|
nextChoice = choice;
|
|
},
|
|
};
|
|
`)();
|
|
|
|
const confirmed = await api.maybeShowNewUserGuidePrompt();
|
|
const modalOptions = api.getModalOptions();
|
|
|
|
assert.equal(confirmed, true);
|
|
assert.equal(api.getDismissed(), '1');
|
|
assert.deepStrictEqual(api.getOpenedUrls(), ['https://apikey.qzz.io']);
|
|
assert.equal(modalOptions.title, '新手引导');
|
|
assert.equal(modalOptions.alert.text, '本提示仅出现一次。');
|
|
assert.deepStrictEqual(
|
|
modalOptions.actions.map((item) => ({ id: item.id, label: item.label })),
|
|
[
|
|
{ id: null, label: '取消' },
|
|
{ id: 'confirm', label: '查看引导' },
|
|
]
|
|
);
|
|
|
|
api.setNextChoice(null);
|
|
const skipped = await api.maybeShowNewUserGuidePrompt();
|
|
assert.equal(skipped, false);
|
|
assert.deepStrictEqual(api.getOpenedUrls(), ['https://apikey.qzz.io']);
|
|
});
|