/* The common attempt policy reads visible scoped DOM, never Angular/network state. */ 'use strict'; const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch({ channel: process.env.PLAYWRIGHT_CHANNEL || 'chrome', headless: true }); const requests = [], errors = []; try { const context = await browser.newContext(); await context.route('**/*', route => { requests.push(route.request().url()); return route.abort(); }); const page = await context.newPage(); page.on('pageerror', error => errors.push(error.message)); await page.addScriptTag({ content: fs.readFileSync(path.join(__dirname, '..', 'attempt-policy.js'), 'utf8') }); async function read(html, tag = 'app-examdetail') { return page.evaluate(({ html, tag }) => { document.body.innerHTML = '<' + tag + ' id="scope">' + html + '' + tag + '>'; return YianAttemptPolicy.read(document.querySelector('#scope')); }, { html, tag }); } const positive = [ '允许答题次数:1次', '总考试次数:1', '最多作答次数为一次', '剩余考试次数:1 次', '可用答题机会:1次', '考试总次数:1次', '答题次数上限:1次', '允许答题次数:仅1次', '考试机会:仅剩1次', '本次仅可答题1次', '仅允许考试一次', '仅有1次答题机会', '本场只剩一次考试机会', '允许作答1次', '总共可考试1次', '还可补考 1 次', '还可补考一次', '剩余答题次数:1次', '剩余 1 次答题机会', '还有一次考试机会', '还可重考1次', '不及格,还可补考 1 次', '(不及格,还可补考 1 次)', '', '', '', '
总考试次数:3次
剩余考试次数:1次
')).count, 1); assert.equal((await read('总考试次数:1次
还可补考0次
')).count, 0); console.log('PASS zero/single attempts stop, larger counts are not blocked, remaining zero wins over total one'); const ignored = [ '已答次数:1次', '已用答题次数:1次', '已考试次数:1次', '已答题数1/40', '已答 1/40', '157人已交卷', '已答题次数:1次,答对 1 题', '答题次数:1', '考试次数:1次', '考试题数:1', '剩余考试次数:1/3', '允许答题次数:1.5次', '允许答题次数:-1次', '还可补考1次是正确答案', '这里只剩1题要答
' ]; for (const html of ignored) { const result = await read(html); assert.equal(result.blocked, false, html); assert.equal(result.count, null, html); assert.equal(result.signature, '[]', html); assert.equal(result.known, false, html); } console.log('PASS used counts, question totals, people, decimals, hidden/answer/question text and private/data attributes do not infer limits'); for (const tag of ['app-examtest', 'div']) assert.equal((await read('剩余考试次数:1次', tag)).count, 1); const before = await read('总考试次数:3次
'); const after = await read('总考试次数:2次
'); assert(before.known && after.known, 'larger explicit count is known even though it does not block'); assert.notEqual(before.signature, after.signature, 'larger explicit count changes invalidate earlier evidence'); const stable = await read('剩余考试次数:1次
还可补考0次
'); assert.equal(stable.signature, (await read('剩余考试次数:1次
')).signature); await page.evaluate(() => { document.querySelector('#scope').insertAdjacentHTML('afterend', '