mirror of https://github.com/zulip/zulip.git
Localstorage: Use `zod` to parse type `FormData`.
Local storage is an untyped interface external to the frontend code itself. The `data` field after `JSON.parse`'d from `raw_data` can be further validated using `zod`'s schema `formDataSchema`. The test case `server_upgrade_alert hide_duration_expired` in `navbar_alerts.test.js` has a bug at `start_time`, which is fixed in this commit. `start_time` is a mock value of `Date.now()` used in `localstorage.ts`, which will concatenate with a number `expires`. So `start_time` was supposed to be an integer value. Before fix, `new Date(1620327447050)` returns a `Date` object which is wrongly concatenated with `expires`. Fixes #24997.
This commit is contained in:
parent
70b8eb3423
commit
fd84651a16
|
@ -1,10 +1,18 @@
|
|||
import {z} from "zod";
|
||||
|
||||
import * as blueslip from "./blueslip";
|
||||
|
||||
type FormData = {
|
||||
data: unknown;
|
||||
__valid: true;
|
||||
expires: number | null;
|
||||
};
|
||||
const formDataSchema = z
|
||||
.object({
|
||||
data: z.unknown(),
|
||||
__valid: z.literal(true),
|
||||
expires: z.number().nullable(),
|
||||
})
|
||||
// z.unknown by default marks the field as optional.
|
||||
// Use zod transform to make optional data field non-optional.
|
||||
.transform((o) => ({data: o.data, ...o}));
|
||||
|
||||
type FormData = z.infer<typeof formDataSchema>;
|
||||
|
||||
export type LocalStorage = {
|
||||
setExpiry(expires: number, isGlobal: boolean): LocalStorage;
|
||||
|
@ -53,18 +61,18 @@ const ls = {
|
|||
return undefined;
|
||||
}
|
||||
data = JSON.parse(raw_data);
|
||||
data = formDataSchema.parse(data);
|
||||
if (
|
||||
// JSON forms of data with `Infinity` turns into `null`,
|
||||
// so if null then it hasn't expired since nothing was specified.
|
||||
data.expires === null ||
|
||||
!ls.isExpired(data.expires)
|
||||
) {
|
||||
return data;
|
||||
}
|
||||
} catch {
|
||||
// data stays undefined
|
||||
}
|
||||
if (
|
||||
data &&
|
||||
data.__valid &&
|
||||
// JSON forms of data with `Infinity` turns into `null`,
|
||||
// so if null then it hasn't expired since nothing was specified.
|
||||
(data.expires === null || !ls.isExpired(data.expires))
|
||||
) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
|
|
@ -91,7 +91,7 @@ test("profile_incomplete_alert", () => {
|
|||
|
||||
test("server_upgrade_alert hide_duration_expired", ({override}) => {
|
||||
const ls = localstorage();
|
||||
const start_time = new Date(1620327447050); // Thursday 06/5/2021 07:02:27 AM (UTC+0)
|
||||
const start_time = 1620327447050; // Thursday 06/5/2021 07:02:27 AM (UTC+0)
|
||||
|
||||
override(Date, "now", () => start_time);
|
||||
assert.equal(ls.get("lastUpgradeNagDismissalTime"), undefined);
|
||||
|
@ -99,7 +99,7 @@ test("server_upgrade_alert hide_duration_expired", ({override}) => {
|
|||
navbar_alerts.dismiss_upgrade_nag(ls);
|
||||
assert.equal(navbar_alerts.should_show_server_upgrade_notification(ls), false);
|
||||
|
||||
override(Date, "now", () => addDays(start_time, 8)); // Friday 14/5/2021 07:02:27 AM (UTC+0)
|
||||
override(Date, "now", () => addDays(start_time, 8).getTime()); // Friday 14/5/2021 07:02:27 AM (UTC+0)
|
||||
assert.equal(navbar_alerts.should_show_server_upgrade_notification(ls), true);
|
||||
navbar_alerts.dismiss_upgrade_nag(ls);
|
||||
assert.equal(navbar_alerts.should_show_server_upgrade_notification(ls), false);
|
||||
|
|
Loading…
Reference in New Issue