feat: add date-based OHIP data reporting alongside XML
This commit is contained in:
1 parent
5ecd571e3b
commit
ad2c59256f
259 files changed
+37326
-126
No files matched your search
@@ -0,0 +1,98 @@
|
||||
// Explicit opt-in against an owned synthetic direct-data portal only.
|
||||
// Credentials/artifacts stay in the supplied private instance; no hotel requests.
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const assert = require('node:assert/strict');
|
||||
const {chromium} = require(process.env.ARR_TEST_PLAYWRIGHT_MODULE || 'playwright');
|
||||
const root = path.resolve(process.env.ARR_REPLAY_TEST_ROOT || '');
|
||||
assert(process.env.ARR_REPLAY_TEST_ROOT, 'explicit private instance required');
|
||||
const instance = JSON.parse(fs.readFileSync(path.join(root, 'instance.json')));
|
||||
assert.equal(instance.version, 'arr-local-direct-data/v1');
|
||||
assert.equal(instance.root, root);
|
||||
assert.equal(instance.oracle_connected, false);
|
||||
const credentials = JSON.parse(fs.readFileSync(path.join(root, 'login.json')));
|
||||
const source = {...instance, source_sha256:instance.fixture_sha256};
|
||||
const mode = 'local-direct-data-simulation';
|
||||
const base = 'http://127.0.0.1:8875';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({headless:true,
|
||||
...(process.env.ARR_TEST_CHROMIUM ? {executablePath:process.env.ARR_TEST_CHROMIUM} : {})});
|
||||
try {
|
||||
const context = await browser.newContext({viewport:{width:1440,height:1000}});
|
||||
const page = await context.newPage();
|
||||
const errors = [];
|
||||
page.on('pageerror', error => errors.push(error.message));
|
||||
await page.goto(base);
|
||||
await page.locator('#username').fill(credentials.username);
|
||||
await page.locator('#password').fill(credentials.password);
|
||||
await page.locator('#login-submit').click();
|
||||
await page.waitForURL(base + '/');
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(await page.locator('#arr-download-date').inputValue(), source.report_date);
|
||||
assert((await page.locator('.local-replay-banner').textContent()).includes('未连接 Oracle'));
|
||||
const health = (await (await context.request.get(base+'/api/health')).json()).data;
|
||||
assert.equal(health.processing_ready, true, 'manual upload remains available');
|
||||
const config = (await (await context.request.get(base+'/api/arr-downloads')).json()).data;
|
||||
assert.equal(config.environment, mode);
|
||||
assert.equal(config.oracle_connected, false);
|
||||
let task = config.latest_task;
|
||||
let submitted = false;
|
||||
if (!task) {
|
||||
const post = page.waitForResponse(r => r.url() === base+'/api/arr-downloads' && r.request().method() === 'POST');
|
||||
await page.locator('#arr-download-button').click();
|
||||
const response = await post;
|
||||
assert.equal(response.status(), 202);
|
||||
task = (await response.json()).data;
|
||||
submitted = true;
|
||||
}
|
||||
const deadline = Date.now()+90000;
|
||||
while (Date.now()<deadline) {
|
||||
task = (await (await context.request.get(base+'/api/arr-downloads/'+task.request_id)).json()).data;
|
||||
if (!['queued','downloading','processing'].includes(task.status)) break;
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
}
|
||||
assert.equal(task.status, 'succeeded');
|
||||
const daily = await context.request.get(base+'/api/download/daily?job_id='+task.job_id);
|
||||
assert.equal(daily.status(), 200);
|
||||
const dailyBytes = await daily.body();
|
||||
assert.equal(dailyBytes.subarray(0,2).toString(), 'PK');
|
||||
fs.writeFileSync(path.join(root, 'verified-daily.xlsx'), dailyBytes, {mode:0o600});
|
||||
let monthly;
|
||||
const monthlyDeadline = Date.now()+90000;
|
||||
while (Date.now()<monthlyDeadline) {
|
||||
const response = await context.request.get(base+'/api/monthly-runs?month='+source.report_date.slice(0,7));
|
||||
assert.equal(response.status(), 200);
|
||||
const rows = (await response.json()).data;
|
||||
if (rows.length) { monthly = rows[0]; break; }
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
}
|
||||
assert(monthly, 'monthly outbox must publish');
|
||||
const monthlyId = monthly.report_id ?? monthly.id;
|
||||
assert(monthlyId, 'monthly result must have an identity');
|
||||
const monthlyResponse = await context.request.get(base+'/api/download/monthly?report_id='+monthlyId);
|
||||
assert.equal(monthlyResponse.status(), 200);
|
||||
const monthlyBytes = await monthlyResponse.body();
|
||||
assert.equal(monthlyBytes.subarray(0,2).toString(), 'PK');
|
||||
fs.writeFileSync(path.join(root, 'verified-monthly.xlsx'), monthlyBytes, {mode:0o600});
|
||||
await page.reload();
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-feedback').classList.contains('is-success'));
|
||||
await page.waitForFunction(() => getComputedStyle(document.querySelector("#panel-daily")).opacity === "1");
|
||||
assert((await page.locator("#jobs-body").textContent()).includes("接口获取"));
|
||||
await page.screenshot({path:path.join(root,'browser-desktop.png'),fullPage:true});
|
||||
fs.chmodSync(path.join(root,'browser-desktop.png'),0o600);
|
||||
await page.setViewportSize({width:390,height:844});
|
||||
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth));
|
||||
await page.screenshot({path:path.join(root,'browser-mobile.png'),fullPage:true});
|
||||
fs.chmodSync(path.join(root,'browser-mobile.png'),0o600);
|
||||
assert.deepEqual(errors, []);
|
||||
const result = {environment:mode, report_date:source.report_date,
|
||||
source_records:source.source_records, source_sha256:source.source_sha256,
|
||||
submitted, task_status:task.status, request_id:task.request_id, job_id:task.job_id,
|
||||
daily_download:true, monthly_download:true, reload_preserved_success:true,
|
||||
mobile_overflow:false, page_errors:0, oracle_connected:false};
|
||||
fs.writeFileSync(path.join(root,'browser-verification.json'),JSON.stringify(result,null,2),{mode:0o600});
|
||||
console.log(JSON.stringify(result));
|
||||
await context.close();
|
||||
} finally { await browser.close(); }
|
||||
})().catch(error => { console.error(error.name, error.message); process.exitCode=1; });
|
||||
@@ -0,0 +1,213 @@
|
||||
// Real browser interaction against an owned read-only loopback fixture. All download
|
||||
// responses are synthetic; no production configuration or hotel service is used.
|
||||
const {test, before, after} = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const {spawn} = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
const {once} = require('node:events');
|
||||
const {chromium} = require(process.env.ARR_TEST_PLAYWRIGHT_MODULE || 'playwright');
|
||||
const root = path.resolve(__dirname, '../..');
|
||||
let fixture, browser, base;
|
||||
before(async () => {
|
||||
fixture = spawn(path.join(root, '.venv/bin/python'), ['-u', '-c', `
|
||||
from http.server import ThreadingHTTPServer
|
||||
from arr_web.preview import ReadOnlyPreview
|
||||
from arr_web.server import handler_for
|
||||
server = ThreadingHTTPServer(('127.0.0.1', 0), handler_for(ReadOnlyPreview()))
|
||||
print(server.server_port, flush=True)
|
||||
server.serve_forever()
|
||||
`], {cwd: root, stdio: ['ignore', 'pipe', 'ignore']});
|
||||
const output = await Promise.race([
|
||||
once(fixture.stdout, 'data').then(([chunk]) => chunk.toString().trim()),
|
||||
once(fixture, 'exit').then(() => { throw new Error('read-only fixture stopped'); }),
|
||||
]);
|
||||
assert.match(output, /^\d+$/);
|
||||
base = `http://127.0.0.1:${output}`;
|
||||
browser = await chromium.launch({headless: true,
|
||||
...(process.env.ARR_TEST_CHROMIUM ? {executablePath: process.env.ARR_TEST_CHROMIUM} : {})});
|
||||
});
|
||||
after(async () => {
|
||||
if (browser) await browser.close();
|
||||
if (fixture && fixture.exitCode === null) { fixture.kill(); await once(fixture, 'exit'); }
|
||||
});
|
||||
const config = (extra={}) => ({ready:true, default_date:'2026-09-15', latest_task:null, ...extra});
|
||||
const fulfill = (route, data) => route.fulfill({json:{ok:true, api_version:'1', data}});
|
||||
async function setup(t, handler, saved=null, beforeGoto=null) {
|
||||
const context = await browser.newContext();
|
||||
t.after(() => context.close());
|
||||
if (saved) await context.addInitScript(saved => localStorage.setItem('arr:last-download:operator', JSON.stringify(saved)), saved);
|
||||
const page = await context.newPage();
|
||||
const errors = [];
|
||||
page.on('pageerror', error => errors.push(error.message));
|
||||
t.after(() => assert.deepEqual(errors, []));
|
||||
await page.clock.install();
|
||||
await page.route('**/api/arr-downloads**', handler);
|
||||
if (beforeGoto) await beforeGoto(page);
|
||||
await page.goto(base);
|
||||
return page;
|
||||
}
|
||||
|
||||
test('initial config failure recovers without reload or automatic POST', async t => {
|
||||
let reads=0, writes=0;
|
||||
const page = await setup(t, route => {
|
||||
if (route.request().method() !== 'GET') { writes++; return route.abort(); }
|
||||
return ++reads === 1 ? route.abort('failed') : fulfill(route, config());
|
||||
});
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('重新连接'));
|
||||
assert(await page.locator('#arr-download-button').isDisabled());
|
||||
await page.clock.runFor(15000);
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert(reads >= 2, 'readiness must be retried after initial GET failure');
|
||||
assert.equal(writes, 0);
|
||||
});
|
||||
|
||||
test('a date chosen while disconnected survives readiness recovery', async t => {
|
||||
let reads=0, writes=0;
|
||||
const page = await setup(t, route => {
|
||||
if (route.request().method() !== 'GET') { writes++; return route.abort(); }
|
||||
return ++reads === 1 ? route.abort('failed') : fulfill(route, config());
|
||||
});
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('重新连接'));
|
||||
await page.fill('#arr-download-date', '2026-08-31');
|
||||
await page.locator('#arr-download-date').blur();
|
||||
await page.clock.runFor(11000);
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(await page.inputValue('#arr-download-date'), '2026-08-31');
|
||||
assert.equal(writes, 0);
|
||||
});
|
||||
|
||||
test('unavailable service becomes ready through GET-only polling', async t => {
|
||||
let reads=0, writes=0;
|
||||
const page = await setup(t, route => {
|
||||
if (route.request().method() !== 'GET') { writes++; return route.abort(); }
|
||||
return fulfill(route, config({ready:++reads > 1}));
|
||||
});
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('暂未就绪'));
|
||||
await page.clock.runFor(11000);
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(reads, 2);
|
||||
assert.equal(writes, 0);
|
||||
});
|
||||
|
||||
const savedIntent = {request_id:'a'.repeat(32), report_date:'2026-09-10'};
|
||||
const interrupted = {...savedIntent, from_date:savedIntent.report_date, to_date:savedIntent.report_date,
|
||||
status:'interrupted', job_id:null, can_retry:true};
|
||||
|
||||
test('initial disconnect preserves the saved task and ignores a different latest task', async t => {
|
||||
let configs=0, writes=0;
|
||||
const queried=[];
|
||||
const page = await setup(t, route => {
|
||||
const request=route.request();
|
||||
if (request.method() !== 'GET') { writes++; return route.abort(); }
|
||||
if (new URL(request.url()).pathname === '/api/arr-downloads') {
|
||||
return ++configs === 1 ? route.abort('failed') : fulfill(route, config({
|
||||
latest_task:{...interrupted, request_id:'b'.repeat(32), report_date:'2026-09-14'},
|
||||
}));
|
||||
}
|
||||
queried.push(request.url().split('/').at(-1));
|
||||
return fulfill(route, interrupted);
|
||||
}, savedIntent);
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('原任务'));
|
||||
assert(await page.locator('#arr-download-date').isEnabled());
|
||||
await page.clock.runFor(11000);
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('任务中断'));
|
||||
assert.equal(await page.inputValue('#arr-download-date'), savedIntent.report_date);
|
||||
assert.deepEqual(queried, [savedIntent.request_id]);
|
||||
assert.equal(writes, 0);
|
||||
const retained=await page.evaluate(() => JSON.parse(localStorage.getItem('arr:last-download:operator')));
|
||||
assert.deepEqual(retained, savedIntent);
|
||||
});
|
||||
|
||||
test('task-status failure rechecks readiness and only an explicit click retries the original task', async t => {
|
||||
let configs=0, lookups=0;
|
||||
const writes=[];
|
||||
const page = await setup(t, route => {
|
||||
const request=route.request(), pathname=new URL(request.url()).pathname;
|
||||
if (request.method() === 'POST') {
|
||||
writes.push({path:pathname, data:request.postDataJSON()});
|
||||
return fulfill(route, interrupted);
|
||||
}
|
||||
if (pathname === '/api/arr-downloads') return fulfill(route, config({ready:++configs !== 2}));
|
||||
return ++lookups === 1 ? route.abort('failed') : fulfill(route, interrupted);
|
||||
}, savedIntent);
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
await page.clock.runFor(11000);
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('暂未就绪'));
|
||||
assert.equal(configs, 2);
|
||||
assert(await page.locator('#arr-download-button').isDisabled());
|
||||
await page.clock.runFor(11000);
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(writes.length, 0);
|
||||
await page.click('#arr-download-button');
|
||||
assert.deepEqual(writes, [{path:`/api/arr-downloads/${savedIntent.request_id}/retry`,data:{}}]);
|
||||
assert.equal(await page.inputValue('#arr-download-date'), savedIntent.report_date);
|
||||
});
|
||||
|
||||
test('online events coalesce an in-flight config request and preserve the edited date', async t => {
|
||||
let configs=0, release;
|
||||
const page = await setup(t, route => {
|
||||
assert.equal(route.request().method(), 'GET');
|
||||
configs++;
|
||||
if (configs === 2) return new Promise(resolve => { release=async () => { await fulfill(route, config()); resolve(); }; });
|
||||
return fulfill(route, config());
|
||||
});
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
await page.fill('#arr-download-date', '2026-08-30');
|
||||
await page.locator('#arr-download-date').blur();
|
||||
await page.evaluate(() => { window.dispatchEvent(new Event('online')); window.dispatchEvent(new Event('online')); });
|
||||
// Synchronize on the intercepted request using the real Node event loop.
|
||||
for (let i=0; !release && i<50; i++) await new Promise(resolve => setTimeout(resolve,20));
|
||||
assert(release);
|
||||
await page.evaluate(() => window.dispatchEvent(new Event('online')));
|
||||
assert.equal(configs, 2);
|
||||
await release();
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(await page.inputValue('#arr-download-date'), '2026-08-30');
|
||||
});
|
||||
|
||||
test('hidden pages pause retries and visibility immediately rechecks the service', async t => {
|
||||
let configs=0;
|
||||
const page = await setup(t, route => { configs++; return fulfill(route, config({ready:configs > 1})); });
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-status').textContent.includes('暂未就绪'));
|
||||
await page.evaluate(() => {
|
||||
Object.defineProperty(document, 'hidden', {configurable:true, get:() => true});
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
});
|
||||
await page.clock.runFor(30000);
|
||||
assert.equal(configs, 1);
|
||||
await page.evaluate(() => {
|
||||
Object.defineProperty(document, 'hidden', {configurable:true, get:() => false});
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
});
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(configs, 2);
|
||||
});
|
||||
|
||||
test('a late initial config does not overwrite a date being edited before blur', async t => {
|
||||
let release;
|
||||
const page = await setup(t, route => new Promise(resolve => {
|
||||
release=async () => { await fulfill(route, config()); resolve(); };
|
||||
}));
|
||||
for (let i=0; !release && i<50; i++) await new Promise(resolve => setTimeout(resolve,20));
|
||||
assert(release);
|
||||
await page.fill('#arr-download-date', '2026-08-29');
|
||||
await release();
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(await page.inputValue('#arr-download-date'), '2026-08-29');
|
||||
});
|
||||
|
||||
test('online before session initialization cannot consume another storage key or issue config calls', async t => {
|
||||
let reads=0, release;
|
||||
const page = await setup(t, route => { reads++; return fulfill(route, config()); }, null, page =>
|
||||
page.route('**/api/session', route => new Promise(resolve => {
|
||||
release=async () => { await route.continue(); resolve(); };
|
||||
})));
|
||||
for (let i=0; !release && i<50; i++) await new Promise(resolve => setTimeout(resolve,20));
|
||||
assert(release);
|
||||
await page.evaluate(() => window.dispatchEvent(new Event('online')));
|
||||
await page.clock.runFor(15000);
|
||||
assert.equal(reads, 0);
|
||||
await release();
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(reads, 1);
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
// Explicit opt-in against an already owned LOCAL XML replay instance only.
|
||||
// Credentials/artifacts stay in the supplied private instance; no hotel requests.
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const assert = require('node:assert/strict');
|
||||
const {chromium} = require(process.env.ARR_TEST_PLAYWRIGHT_MODULE || 'playwright');
|
||||
const root = path.resolve(process.env.ARR_REPLAY_TEST_ROOT || '');
|
||||
assert(process.env.ARR_REPLAY_TEST_ROOT, 'explicit private instance required');
|
||||
const instance = JSON.parse(fs.readFileSync(path.join(root, 'instance.json')));
|
||||
assert.equal(instance.version, 'arr-local-replay-instance/v1');
|
||||
assert.equal(instance.root, root);
|
||||
const credentials = JSON.parse(fs.readFileSync(path.join(root, 'login.json')));
|
||||
const source = JSON.parse(fs.readFileSync(path.join(root, 'fixture/source.json')));
|
||||
const mode = process.env.ARR_LOCAL_TEST_MODE || 'local-xml-replay';
|
||||
assert(['local-xml-replay', 'local-api-simulation'].includes(mode));
|
||||
if (mode === 'local-api-simulation') {
|
||||
const simulation = JSON.parse(fs.readFileSync(path.join(root, 'simulation.json')));
|
||||
assert.equal(simulation.version, 'local-arr-api-fixture/v1');
|
||||
assert.equal(simulation.oracle_connected, false);
|
||||
assert.equal(simulation.source_sha256, source.source_sha256);
|
||||
} else {
|
||||
assert(!fs.existsSync(path.join(root, 'simulation.json')));
|
||||
}
|
||||
const base = mode === 'local-api-simulation' ? 'http://127.0.0.1:8874' : 'http://127.0.0.1:8873';
|
||||
|
||||
(async () => {
|
||||
const browser = await chromium.launch({headless:true,
|
||||
...(process.env.ARR_TEST_CHROMIUM ? {executablePath:process.env.ARR_TEST_CHROMIUM} : {})});
|
||||
try {
|
||||
const context = await browser.newContext({viewport:{width:1440,height:1000}});
|
||||
const page = await context.newPage();
|
||||
const errors = [];
|
||||
page.on('pageerror', error => errors.push(error.message));
|
||||
await page.goto(base);
|
||||
await page.locator('#username').fill(credentials.username);
|
||||
await page.locator('#password').fill(credentials.password);
|
||||
await page.locator('#login-submit').click();
|
||||
await page.waitForURL(base + '/');
|
||||
await page.waitForFunction(() => !document.querySelector('#arr-download-button').disabled);
|
||||
assert.equal(await page.locator('#arr-download-date').inputValue(), source.report_date);
|
||||
assert((await page.locator('.local-replay-banner').textContent()).includes('未连接 Oracle'));
|
||||
const config = (await (await context.request.get(base+'/api/arr-downloads')).json()).data;
|
||||
assert.equal(config.environment, mode);
|
||||
assert.equal(config.oracle_connected, false);
|
||||
let task = config.latest_task;
|
||||
let submitted = false;
|
||||
if (!task) {
|
||||
const post = page.waitForResponse(r => r.url() === base+'/api/arr-downloads' && r.request().method() === 'POST');
|
||||
await page.locator('#arr-download-button').click();
|
||||
const response = await post;
|
||||
assert.equal(response.status(), 202);
|
||||
task = (await response.json()).data;
|
||||
submitted = true;
|
||||
}
|
||||
const deadline = Date.now()+90000;
|
||||
while (Date.now()<deadline) {
|
||||
task = (await (await context.request.get(base+'/api/arr-downloads/'+task.request_id)).json()).data;
|
||||
if (!['queued','downloading','processing'].includes(task.status)) break;
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
}
|
||||
assert.equal(task.status, 'succeeded');
|
||||
const daily = await context.request.get(base+'/api/download/daily?job_id='+task.job_id);
|
||||
assert.equal(daily.status(), 200);
|
||||
const dailyBytes = await daily.body();
|
||||
assert.equal(dailyBytes.subarray(0,2).toString(), 'PK');
|
||||
fs.writeFileSync(path.join(root, 'verified-daily.xlsx'), dailyBytes, {mode:0o600});
|
||||
let monthly;
|
||||
const monthlyDeadline = Date.now()+90000;
|
||||
while (Date.now()<monthlyDeadline) {
|
||||
const response = await context.request.get(base+'/api/monthly-runs?month='+source.report_date.slice(0,7));
|
||||
assert.equal(response.status(), 200);
|
||||
const rows = (await response.json()).data;
|
||||
if (rows.length) { monthly = rows[0]; break; }
|
||||
await new Promise(r => setTimeout(r, 300));
|
||||
}
|
||||
assert(monthly, 'monthly outbox must publish');
|
||||
const monthlyId = monthly.report_id ?? monthly.id;
|
||||
assert(monthlyId, 'monthly result must have an identity');
|
||||
const monthlyResponse = await context.request.get(base+'/api/download/monthly?report_id='+monthlyId);
|
||||
assert.equal(monthlyResponse.status(), 200);
|
||||
const monthlyBytes = await monthlyResponse.body();
|
||||
assert.equal(monthlyBytes.subarray(0,2).toString(), 'PK');
|
||||
fs.writeFileSync(path.join(root, 'verified-monthly.xlsx'), monthlyBytes, {mode:0o600});
|
||||
await page.reload();
|
||||
await page.waitForFunction(() => document.querySelector('#arr-download-feedback').classList.contains('is-success'));
|
||||
await page.screenshot({path:path.join(root,'browser-desktop.png'),fullPage:true});
|
||||
fs.chmodSync(path.join(root,'browser-desktop.png'),0o600);
|
||||
await page.setViewportSize({width:390,height:844});
|
||||
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth));
|
||||
await page.screenshot({path:path.join(root,'browser-mobile.png'),fullPage:true});
|
||||
fs.chmodSync(path.join(root,'browser-mobile.png'),0o600);
|
||||
assert.deepEqual(errors, []);
|
||||
const result = {environment:mode, report_date:source.report_date,
|
||||
source_records:source.source_records, source_sha256:source.source_sha256,
|
||||
submitted, task_status:task.status, request_id:task.request_id, job_id:task.job_id,
|
||||
daily_download:true, monthly_download:true, reload_preserved_success:true,
|
||||
mobile_overflow:false, page_errors:0, oracle_connected:false};
|
||||
fs.writeFileSync(path.join(root,'browser-verification.json'),JSON.stringify(result,null,2),{mode:0o600});
|
||||
console.log(JSON.stringify(result));
|
||||
await context.close();
|
||||
} finally { await browser.close(); }
|
||||
})().catch(error => { console.error(error.name, error.message); process.exitCode=1; });
|
||||
Reference in new issue
Block a user