fix(replay): preserve Date call and explicit constructor semantics (#512)
Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
parent
5b28a8b331
commit
9975bd23cd
|
|
@ -276,8 +276,13 @@ const deterministicContext = (request: WorkerRequest, caseIndex: number) => {
|
|||
};
|
||||
const epoch = Date.parse(request.determinism.clockIso);
|
||||
class ReplayDate extends Date {
|
||||
constructor(...arguments_: [] | [string | number]) {
|
||||
super(arguments_.length === 0 ? epoch : arguments_[0]);
|
||||
constructor(...arguments_: unknown[]) {
|
||||
super(epoch);
|
||||
if (arguments_.length > 0) {
|
||||
const date: unknown = Reflect.construct(Date, arguments_, new.target);
|
||||
if (!types.isDate(date)) throw new TypeError("Invalid Date instance");
|
||||
return date;
|
||||
}
|
||||
}
|
||||
static override now(): number {
|
||||
return epoch;
|
||||
|
|
@ -286,7 +291,12 @@ const deterministicContext = (request: WorkerRequest, caseIndex: number) => {
|
|||
const replayMath: Math = Object.create(Math);
|
||||
Object.defineProperty(replayMath, "random", { value: random });
|
||||
return createContext(
|
||||
{ Date: ReplayDate, Math: replayMath },
|
||||
{
|
||||
Date: new Proxy(ReplayDate, {
|
||||
apply: () => new ReplayDate().toString(),
|
||||
}),
|
||||
Math: replayMath,
|
||||
},
|
||||
{
|
||||
name: "rea-controlled-replay",
|
||||
codeGeneration: { strings: false, wasm: false },
|
||||
|
|
|
|||
|
|
@ -226,6 +226,41 @@ describe("disposable JavaScript replay worker", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("deterministic replay Date compatibility", () => {
|
||||
it.each([
|
||||
["new Date(2020, 0, 2).getFullYear()", 2020],
|
||||
["new Date(2020, 0, 2, 3, 4, 5, 6).getMilliseconds()", 6],
|
||||
["typeof Date()", "string"],
|
||||
["Date() === new Date().toString()", true],
|
||||
["new Date().toISOString()", "2000-01-01T00:00:00.000Z"],
|
||||
["Date.now()", 946684800000],
|
||||
["new Date(0).getTime()", 0],
|
||||
[
|
||||
"new Date('2020-01-02T00:00:00Z').toISOString()",
|
||||
"2020-01-02T00:00:00.000Z",
|
||||
],
|
||||
["new Date() instanceof Date", true],
|
||||
[
|
||||
"(() => { class ChildDate extends Date {} const date = new ChildDate(2020, 0, 2); return [date instanceof ChildDate, date instanceof Date, date.getFullYear(), date.constructor === ChildDate]; })()",
|
||||
[true, true, 2020, true],
|
||||
],
|
||||
["Date.UTC(2020, 0, 2)", 1577923200000],
|
||||
])("preserves %s", async (expression, expected) => {
|
||||
const workerRequest = await request("parser.mjs", "esm", "default", []);
|
||||
workerRequest.left.modules[0] = {
|
||||
alias: "entry",
|
||||
format: "esm",
|
||||
dependencies: {},
|
||||
source: `export default function () { return ${expression}; }`,
|
||||
};
|
||||
const result = await runWorker(workerRequest);
|
||||
expect(result.code).toBe(0);
|
||||
expect(JSON.parse(result.stdout)).toMatchObject({
|
||||
left: [{ outcome: "return", value: expected }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("replay array projection", () => {
|
||||
it.each([
|
||||
["Array(3)", [null, null, null]],
|
||||
|
|
|
|||
Loading…
Reference in New Issue