Files
Investidor-V2/tests/frontend/interest-draft-storage.test.mjs
T
2026-11-15 18:56:39 +02:00

162 lines
5.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { compileFunction } from 'node:vm';
import ts from 'typescript';
await test('blocked browser storage cannot prevent authentication or clearing a submitted interest', () => {
const source = fs.readFileSync(
new URL(
'../../resources/js/hooks/use-interest-draft.ts',
import.meta.url,
),
'utf8',
);
const output = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2022,
},
}).outputText;
const module = { exports: {} };
let destination;
const storage = {
getItem() {
throw new Error('blocked');
},
setItem() {
throw new Error('blocked');
},
removeItem() {
throw new Error('blocked');
},
};
compileFunction(output, [
'require',
'module',
'exports',
'sessionStorage',
'window',
'location',
])(
(name) =>
name === 'react'
? {
useState: (init) => [init(), () => {}],
useEffect: (effect) => effect(),
}
: { newSubmissionKey: () => 'synthetic-key' },
module,
module.exports,
storage,
{
location: {
assign: (url) => {
destination = url;
},
},
},
{ pathname: '/oportunidades/teste', search: '?sector=1' },
);
const draft = module.exports.useInterestDraft('synthetic', {
name: 'Synthetic',
email: 'synthetic@example.invalid',
});
assert.equal(draft.key, 'synthetic-key');
assert.doesNotThrow(() => draft.clear());
assert.doesNotThrow(() => draft.authenticate());
assert.equal(
destination,
'/participar/entrar?return=%2Foportunidades%2Fteste%3Fsector%3D1',
);
});
test('reused interest hook isolates opportunities, retains deliberate edits and restores after reload', () => {
const source = fs.readFileSync(
new URL(
'../../resources/js/hooks/use-interest-draft.ts',
import.meta.url,
),
'utf8',
);
const output = ts.transpileModule(source, {
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2022,
},
}).outputText;
const saved = new Map();
let sequence = 0;
function mount(storage = saved) {
let value, changed, effect;
const module = { exports: {} };
compileFunction(output, [
'require',
'module',
'exports',
'sessionStorage',
'window',
'location',
])(
(name) =>
name === 'react'
? {
useState(init) {
value ??= init();
return [
value,
(next) => {
value =
typeof next === 'function'
? next(value)
: next;
changed = true;
},
];
},
useEffect(callback) {
effect = callback;
},
}
: { newSubmissionKey: () => 'synthetic-' + ++sequence },
module,
module.exports,
{
getItem: (key) => storage.get(key) ?? null,
setItem: (key, data) => storage.set(key, data),
removeItem: (key) => storage.delete(key),
},
{ location: { assign() {} } },
{ pathname: '/negocios/a', search: '?province_id=p' },
);
return (name, initial = { message: 'initial ' + name }) => {
changed = false;
let result = module.exports.useInterestDraft(name, initial);
// React discards a render-phase reset and renders the new scoped state.
if (changed)
result = module.exports.useInterestDraft(name, initial);
effect();
return result;
};
}
const render = mount();
const a = render('business:A');
a.setForm({ message: 'deliberate A' });
render('business:A');
const b = render('business:B');
assert.equal(b.form.message, 'initial business:B');
assert.notEqual(a.key, b.key);
b.setForm({ message: 'deliberate B' });
render('business:B');
assert.equal(render('business:A').form.message, 'deliberate A');
assert.equal(mount()('business:B').form.message, 'deliberate B');
assert.equal(
mount(new Map())('business:A').form.message,
'initial business:A',
);
assert.equal(
JSON.parse(saved.get('investemoz-interest:business:A')).form.message,
'deliberate A',
);
});