添加自动运行广告条的样式和逻辑,并实现相关功能的测试
This commit is contained in:
@@ -652,6 +652,109 @@ header {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 34px;
|
||||
padding: 7px 46px;
|
||||
border: 1px solid color-mix(in srgb, var(--amber) 24%, var(--border));
|
||||
border-radius: var(--radius-sm);
|
||||
background: color-mix(in srgb, var(--amber) 10%, var(--bg-base));
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bg-base) 75%, transparent);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.auto-run-ad-badge {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 3px 7px;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--amber) 22%, var(--bg-base));
|
||||
color: color-mix(in srgb, var(--amber) 80%, var(--text-primary));
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.auto-run-ad-viewport {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.auto-run-ad-track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: max-content;
|
||||
min-width: 100%;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.auto-run-ad-text {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.auto-run-ad-text--clone {
|
||||
display: none;
|
||||
padding-left: var(--auto-run-ad-gap, 28px);
|
||||
}
|
||||
|
||||
.auto-run-ad-link {
|
||||
color: color-mix(in srgb, var(--amber) 80%, var(--link));
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 2px;
|
||||
transition: color var(--transition);
|
||||
}
|
||||
|
||||
.auto-run-ad-link:hover {
|
||||
color: var(--link-hover);
|
||||
}
|
||||
|
||||
.auto-run-ad-link:focus-visible {
|
||||
outline: 2px solid color-mix(in srgb, var(--amber) 36%, var(--border));
|
||||
outline-offset: 2px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar.is-scrolling .auto-run-ad-track {
|
||||
justify-content: flex-start;
|
||||
min-width: max-content;
|
||||
animation: auto-run-ad-marquee var(--auto-run-ad-duration, 16s) linear infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar.is-scrolling:hover .auto-run-ad-track,
|
||||
.auto-run-ad-bar.is-scrolling:focus-within .auto-run-ad-track {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.auto-run-ad-bar.is-scrolling .auto-run-ad-text--clone {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes auto-run-ad-marquee {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(calc(-1 * var(--auto-run-ad-scroll-distance, 0px)));
|
||||
}
|
||||
}
|
||||
|
||||
.contribution-mode-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -124,6 +124,15 @@
|
||||
<option value="sub2api">SUB2API</option>
|
||||
<option value="codex2api">Codex2API</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="auto-run-ad-bar" class="auto-run-ad-bar" hidden aria-live="polite">
|
||||
<span class="auto-run-ad-badge">广告</span>
|
||||
<div id="auto-run-ad-viewport" class="auto-run-ad-viewport">
|
||||
<div id="auto-run-ad-track" class="auto-run-ad-track">
|
||||
<span id="auto-run-ad-text" class="auto-run-ad-text"></span>
|
||||
<span id="auto-run-ad-text-clone" class="auto-run-ad-text auto-run-ad-text--clone" aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
|
||||
<div class="contribution-mode-panel-header">
|
||||
|
||||
+249
-4
@@ -66,6 +66,11 @@ const contributionUpdateLayer = document.getElementById('contribution-update-lay
|
||||
const contributionUpdateHint = document.getElementById('contribution-update-hint');
|
||||
const contributionUpdateHintText = document.getElementById('contribution-update-hint-text');
|
||||
const btnDismissContributionUpdateHint = document.getElementById('btn-dismiss-contribution-update-hint');
|
||||
const autoRunAdBar = document.getElementById('auto-run-ad-bar');
|
||||
const autoRunAdViewport = document.getElementById('auto-run-ad-viewport');
|
||||
const autoRunAdTrack = document.getElementById('auto-run-ad-track');
|
||||
const autoRunAdText = document.getElementById('auto-run-ad-text');
|
||||
const autoRunAdTextClone = document.getElementById('auto-run-ad-text-clone');
|
||||
const stepsProgress = document.getElementById('steps-progress');
|
||||
const btnAutoRun = document.getElementById('btn-auto-run');
|
||||
const btnAutoContinue = document.getElementById('btn-auto-continue');
|
||||
@@ -1346,6 +1351,7 @@ let configActionInFlight = false;
|
||||
let currentReleaseSnapshot = null;
|
||||
let currentContributionContentSnapshot = null;
|
||||
let contributionContentSnapshotRequestInFlight = null;
|
||||
let autoRunAdScrollSyncFrame = 0;
|
||||
|
||||
function normalizeAutomationWindowId(value) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
@@ -11013,6 +11019,156 @@ function getContributionUpdatePromptLines(snapshot = currentContributionContentS
|
||||
return lines;
|
||||
}
|
||||
|
||||
function getAutoRunAdConfig(snapshot = currentContributionContentSnapshot) {
|
||||
const items = Array.isArray(snapshot?.items) ? snapshot.items : [];
|
||||
const adItem = items.find((item) =>
|
||||
item
|
||||
&& String(item.slug || '').trim().toLowerCase() === 'extension_auto_run_ad'
|
||||
);
|
||||
if (!adItem || !adItem.isVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const text = String(adItem.text || '').replace(/\s+/g, ' ').trim();
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
text,
|
||||
title: String(adItem.title || '').trim(),
|
||||
};
|
||||
}
|
||||
|
||||
function sanitizeAutoRunAdUrl(value = '') {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = new URL(raw);
|
||||
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||
return '';
|
||||
}
|
||||
return parsed.href;
|
||||
} catch (_error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function parseAutoRunAdSegments(value = '') {
|
||||
const rawText = String(value || '').trim();
|
||||
if (!rawText) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const segments = [];
|
||||
let cursor = 0;
|
||||
|
||||
while (cursor < rawText.length) {
|
||||
const labelStart = rawText.indexOf('[', cursor);
|
||||
if (labelStart < 0) {
|
||||
segments.push({
|
||||
type: 'text',
|
||||
text: rawText.slice(cursor),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
const labelEnd = rawText.indexOf(']', labelStart + 1);
|
||||
if (labelEnd < 0 || rawText[labelEnd + 1] !== '(') {
|
||||
segments.push({
|
||||
type: 'text',
|
||||
text: rawText.slice(cursor),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (labelStart > cursor) {
|
||||
segments.push({
|
||||
type: 'text',
|
||||
text: rawText.slice(cursor, labelStart),
|
||||
});
|
||||
}
|
||||
|
||||
let urlEnd = labelEnd + 2;
|
||||
let depth = 1;
|
||||
while (urlEnd < rawText.length && depth > 0) {
|
||||
const ch = rawText[urlEnd];
|
||||
if (ch === '(') {
|
||||
depth += 1;
|
||||
} else if (ch === ')') {
|
||||
depth -= 1;
|
||||
}
|
||||
urlEnd += 1;
|
||||
}
|
||||
|
||||
if (depth > 0) {
|
||||
segments.push({
|
||||
type: 'text',
|
||||
text: rawText.slice(labelStart),
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
const label = rawText.slice(labelStart + 1, labelEnd).trim();
|
||||
const rawSegment = rawText.slice(labelStart, urlEnd);
|
||||
const url = sanitizeAutoRunAdUrl(rawText.slice(labelEnd + 2, urlEnd - 1));
|
||||
if (label && url) {
|
||||
segments.push({
|
||||
type: 'link',
|
||||
text: label,
|
||||
url,
|
||||
});
|
||||
} else {
|
||||
segments.push({
|
||||
type: 'text',
|
||||
text: rawSegment,
|
||||
});
|
||||
}
|
||||
cursor = urlEnd;
|
||||
}
|
||||
|
||||
return segments.filter((segment) => String(segment?.text || '').length > 0);
|
||||
}
|
||||
|
||||
function getAutoRunAdPlainText(segments = []) {
|
||||
return segments.map((segment) => String(segment?.text || '')).join('').replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
function renderAutoRunAdSegments(container, segments = [], options = {}) {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { tabIndex = undefined } = options;
|
||||
container.textContent = '';
|
||||
for (const segment of Array.isArray(segments) ? segments : []) {
|
||||
const text = String(segment?.text || '');
|
||||
if (!text) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (segment?.type === 'link' && segment?.url) {
|
||||
const link = document.createElement('a');
|
||||
link.className = 'auto-run-ad-link';
|
||||
link.href = segment.url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
link.dataset.externalUrl = segment.url;
|
||||
if (Number.isInteger(tabIndex)) {
|
||||
link.tabIndex = tabIndex;
|
||||
}
|
||||
link.textContent = text;
|
||||
container.appendChild(link);
|
||||
continue;
|
||||
}
|
||||
|
||||
container.appendChild(document.createTextNode(text));
|
||||
}
|
||||
}
|
||||
|
||||
function positionContributionUpdateHint() {
|
||||
if (!contributionUpdateLayer || !contributionUpdateHint || !btnContributionMode) {
|
||||
return;
|
||||
@@ -11086,6 +11242,86 @@ function renderContributionUpdateHint(snapshot = currentContributionContentSnaps
|
||||
positionContributionUpdateHint();
|
||||
}
|
||||
|
||||
function resetAutoRunAdScrollState() {
|
||||
if (!autoRunAdBar || !autoRunAdTrack || !autoRunAdTextClone) {
|
||||
return;
|
||||
}
|
||||
autoRunAdBar.classList.remove('is-scrolling');
|
||||
autoRunAdBar.style.removeProperty('--auto-run-ad-gap');
|
||||
autoRunAdBar.style.removeProperty('--auto-run-ad-duration');
|
||||
autoRunAdBar.style.removeProperty('--auto-run-ad-scroll-distance');
|
||||
autoRunAdTextClone.textContent = '';
|
||||
}
|
||||
|
||||
function syncAutoRunAdScrollState() {
|
||||
if (!autoRunAdBar || !autoRunAdViewport || !autoRunAdTrack || !autoRunAdText || !autoRunAdTextClone || autoRunAdBar.hidden) {
|
||||
resetAutoRunAdScrollState();
|
||||
return;
|
||||
}
|
||||
|
||||
const text = String(autoRunAdText.textContent || '').trim();
|
||||
if (!text) {
|
||||
resetAutoRunAdScrollState();
|
||||
return;
|
||||
}
|
||||
|
||||
const viewportWidth = autoRunAdViewport.clientWidth || 0;
|
||||
const textWidth = Math.ceil(autoRunAdText.getBoundingClientRect().width || 0);
|
||||
if (!viewportWidth || !textWidth || textWidth <= viewportWidth) {
|
||||
resetAutoRunAdScrollState();
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollGap = 32;
|
||||
const scrollDistance = textWidth + scrollGap;
|
||||
const durationSeconds = Math.max(18, scrollDistance / 24);
|
||||
autoRunAdBar.classList.add('is-scrolling');
|
||||
autoRunAdBar.style.setProperty('--auto-run-ad-gap', `${scrollGap}px`);
|
||||
autoRunAdBar.style.setProperty('--auto-run-ad-duration', `${durationSeconds.toFixed(2)}s`);
|
||||
autoRunAdBar.style.setProperty('--auto-run-ad-scroll-distance', `${scrollDistance}px`);
|
||||
}
|
||||
|
||||
function scheduleAutoRunAdScrollSync() {
|
||||
if (autoRunAdScrollSyncFrame && typeof window.cancelAnimationFrame === 'function') {
|
||||
window.cancelAnimationFrame(autoRunAdScrollSyncFrame);
|
||||
autoRunAdScrollSyncFrame = 0;
|
||||
}
|
||||
|
||||
if (typeof window.requestAnimationFrame === 'function') {
|
||||
autoRunAdScrollSyncFrame = window.requestAnimationFrame(() => {
|
||||
autoRunAdScrollSyncFrame = 0;
|
||||
syncAutoRunAdScrollState();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
syncAutoRunAdScrollState();
|
||||
}
|
||||
|
||||
function renderAutoRunAd(snapshot = currentContributionContentSnapshot) {
|
||||
if (!autoRunAdBar || !autoRunAdText || !autoRunAdTextClone) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = getAutoRunAdConfig(snapshot);
|
||||
const visible = Boolean(config);
|
||||
autoRunAdBar.hidden = !visible;
|
||||
if (!visible) {
|
||||
autoRunAdBar.title = '';
|
||||
autoRunAdText.textContent = '';
|
||||
autoRunAdTextClone.textContent = '';
|
||||
resetAutoRunAdScrollState();
|
||||
return;
|
||||
}
|
||||
|
||||
const segments = parseAutoRunAdSegments(config.text);
|
||||
const plainText = getAutoRunAdPlainText(segments);
|
||||
autoRunAdBar.title = plainText || config.text;
|
||||
renderAutoRunAdSegments(autoRunAdText, segments);
|
||||
renderAutoRunAdSegments(autoRunAdTextClone, segments, { tabIndex: -1 });
|
||||
scheduleAutoRunAdScrollSync();
|
||||
}
|
||||
|
||||
function dismissContributionUpdateHint() {
|
||||
const promptVersion = String(currentContributionContentSnapshot?.promptVersion || '').trim();
|
||||
if (promptVersion) {
|
||||
@@ -11098,6 +11334,7 @@ async function refreshContributionContentHint() {
|
||||
if (!contributionContentService?.getContentUpdateSnapshot) {
|
||||
currentContributionContentSnapshot = null;
|
||||
renderContributionUpdateHint();
|
||||
renderAutoRunAd();
|
||||
return null;
|
||||
}
|
||||
if (contributionContentSnapshotRequestInFlight) {
|
||||
@@ -11108,11 +11345,13 @@ async function refreshContributionContentHint() {
|
||||
.then((snapshot) => {
|
||||
currentContributionContentSnapshot = snapshot;
|
||||
renderContributionUpdateHint(snapshot);
|
||||
renderAutoRunAd(snapshot);
|
||||
return snapshot;
|
||||
})
|
||||
.catch((error) => {
|
||||
currentContributionContentSnapshot = null;
|
||||
renderContributionUpdateHint(null);
|
||||
renderAutoRunAd(null);
|
||||
throw error;
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -13631,6 +13870,12 @@ autoStartMessage?.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
openExternalUrl(link.dataset.externalUrl || link.href);
|
||||
});
|
||||
autoRunAdBar?.addEventListener('click', (event) => {
|
||||
const link = event.target?.closest?.('a[data-external-url]');
|
||||
if (!link) return;
|
||||
event.preventDefault();
|
||||
openExternalUrl(link.dataset.externalUrl || link.href);
|
||||
});
|
||||
btnAutoStartClose?.addEventListener('click', () => resolveModalChoice(null));
|
||||
|
||||
async function startAutoRunFromCurrentSettings() {
|
||||
@@ -13642,11 +13887,10 @@ async function startAutoRunFromCurrentSettings() {
|
||||
: getRunCountValue();
|
||||
registerPendingAutoRunStartRunCount(requestedTotalRuns);
|
||||
|
||||
try {
|
||||
await refreshContributionContentHint();
|
||||
} catch (error) {
|
||||
// 站点内容刷新只影响提示/广告展示,不应阻塞自动流程启动。
|
||||
refreshContributionContentHint().catch((error) => {
|
||||
console.warn('Failed to refresh contribution content hint before auto run:', error);
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof persistCurrentSettingsForAction === 'function') {
|
||||
await persistCurrentSettingsForAction();
|
||||
@@ -16892,6 +17136,7 @@ document.addEventListener('keydown', (event) => {
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
positionContributionUpdateHint();
|
||||
scheduleAutoRunAdScrollSync();
|
||||
});
|
||||
|
||||
document.addEventListener('scroll', () => {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
|
||||
|
||||
function extractFunction(name) {
|
||||
const markers = [`async function ${name}(`, `function ${name}(`];
|
||||
const start = markers
|
||||
.map((marker) => sidepanelSource.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 < sidepanelSource.length; i += 1) {
|
||||
const ch = sidepanelSource[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 < sidepanelSource.length; end += 1) {
|
||||
const ch = sidepanelSource[end];
|
||||
if (ch === '{') depth += 1;
|
||||
if (ch === '}') {
|
||||
depth -= 1;
|
||||
if (depth === 0) {
|
||||
end += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sidepanelSource.slice(start, end);
|
||||
}
|
||||
|
||||
const bundle = [
|
||||
extractFunction('sanitizeAutoRunAdUrl'),
|
||||
extractFunction('parseAutoRunAdSegments'),
|
||||
extractFunction('getAutoRunAdPlainText'),
|
||||
].join('\n');
|
||||
|
||||
const api = new Function(`
|
||||
${bundle}
|
||||
return {
|
||||
sanitizeAutoRunAdUrl,
|
||||
parseAutoRunAdSegments,
|
||||
getAutoRunAdPlainText,
|
||||
};
|
||||
`)();
|
||||
|
||||
test('sanitizeAutoRunAdUrl only allows http and https links', () => {
|
||||
assert.equal(api.sanitizeAutoRunAdUrl('https://example.com/docs'), 'https://example.com/docs');
|
||||
assert.equal(api.sanitizeAutoRunAdUrl('http://example.com/docs'), 'http://example.com/docs');
|
||||
assert.equal(api.sanitizeAutoRunAdUrl('javascript:alert(1)'), '');
|
||||
assert.equal(api.sanitizeAutoRunAdUrl('data:text/html,hello'), '');
|
||||
});
|
||||
|
||||
test('parseAutoRunAdSegments converts markdown links into safe link segments', () => {
|
||||
const segments = api.parseAutoRunAdSegments('查看 [教程](https://example.com/tutorial) 和 [频道](https://example.com/channel)');
|
||||
|
||||
assert.deepEqual(segments, [
|
||||
{ type: 'text', text: '查看 ' },
|
||||
{ type: 'link', text: '教程', url: 'https://example.com/tutorial' },
|
||||
{ type: 'text', text: ' 和 ' },
|
||||
{ type: 'link', text: '频道', url: 'https://example.com/channel' },
|
||||
]);
|
||||
assert.equal(api.getAutoRunAdPlainText(segments), '查看 教程 和 频道');
|
||||
});
|
||||
|
||||
test('parseAutoRunAdSegments keeps unsafe markdown links as plain text', () => {
|
||||
const segments = api.parseAutoRunAdSegments('危险链接 [不要点](javascript:alert(1))');
|
||||
|
||||
assert.deepEqual(segments, [
|
||||
{ type: 'text', text: '危险链接 ' },
|
||||
{ type: 'text', text: '[不要点](javascript:alert(1))' },
|
||||
]);
|
||||
});
|
||||
@@ -151,7 +151,7 @@ return {
|
||||
`)();
|
||||
}
|
||||
|
||||
test('startAutoRunFromCurrentSettings refreshes contribution content hint before starting auto run', async () => {
|
||||
test('startAutoRunFromCurrentSettings kicks off contribution content refresh without blocking auto run', async () => {
|
||||
const api = createApi();
|
||||
|
||||
const result = await api.startAutoRunFromCurrentSettings();
|
||||
@@ -175,10 +175,24 @@ test('startAutoRunFromCurrentSettings continues auto run when contribution conte
|
||||
assert.equal(result, true);
|
||||
assert.deepEqual(
|
||||
events.map((entry) => entry.type),
|
||||
['refresh', 'warn', 'sync-settings', 'send']
|
||||
['refresh', 'sync-settings', 'send', 'warn']
|
||||
);
|
||||
assert.equal(events[2].message.type, 'AUTO_RUN');
|
||||
assert.match(String(events[3].args[0]), /Failed to refresh contribution content hint before auto run/);
|
||||
});
|
||||
|
||||
test('startAutoRunFromCurrentSettings does not wait for a hanging contribution content refresh', async () => {
|
||||
const api = createApi({
|
||||
refreshImpl: '() => new Promise(() => {})',
|
||||
});
|
||||
|
||||
const result = await api.startAutoRunFromCurrentSettings();
|
||||
|
||||
assert.equal(result, true);
|
||||
assert.deepEqual(
|
||||
api.getEvents().map((entry) => entry.type),
|
||||
['refresh', 'sync-settings', 'send']
|
||||
);
|
||||
assert.match(String(events[1].args[0]), /Failed to refresh contribution content hint before auto run/);
|
||||
assert.equal(events[3].message.type, 'AUTO_RUN');
|
||||
});
|
||||
|
||||
test('startAutoRunFromCurrentSettings does not block auto run when contribution content has updates', async () => {
|
||||
|
||||
Reference in New Issue
Block a user