70bf21e064
Deploy (to testing) and Test Playground Preview Worker / Deploy Playground Preview Worker (testing) (push) Has been skipped
Deploy Workers Shared Staging / Deploy Workers Shared Staging (push) Failing after 0s
Prerelease / build (push) Has been skipped
Handle Changesets / Handle Changesets (push) Has been cancelled
Semgrep OSS scan / semgrep-oss (push) Has been cancelled
407 lines
8.9 KiB
TypeScript
407 lines
8.9 KiB
TypeScript
import { describe, it } from "vitest";
|
|
import {
|
|
indexLocation,
|
|
parseByteSize,
|
|
parseJSON,
|
|
parseJSONC,
|
|
parseTOML,
|
|
searchLocation,
|
|
} from "../src/parse";
|
|
|
|
describe("parseTOML", () => {
|
|
it("should parse toml that is empty", ({ expect }) => {
|
|
expect(parseTOML("")).toStrictEqual({});
|
|
});
|
|
|
|
it("should parse toml with basic values", ({ expect }) => {
|
|
expect(
|
|
parseTOML(`
|
|
name = "basic"
|
|
version = 1
|
|
`)
|
|
).toStrictEqual({
|
|
name: "basic",
|
|
version: 1,
|
|
});
|
|
});
|
|
|
|
it("should parse toml with complex values", ({ expect }) => {
|
|
expect(
|
|
parseTOML(`
|
|
name = 'complex'
|
|
version = 1
|
|
[owner]
|
|
name = ["tim"]
|
|
alive = true
|
|
[owner.dog]
|
|
exists = true
|
|
`)
|
|
).toStrictEqual({
|
|
name: "complex",
|
|
owner: {
|
|
alive: true,
|
|
dog: {
|
|
exists: true,
|
|
},
|
|
name: ["tim"],
|
|
},
|
|
version: 1,
|
|
});
|
|
});
|
|
|
|
it("should fail to parse toml with invalid string", ({ expect }) => {
|
|
try {
|
|
parseTOML(`name = 'fail"`);
|
|
expect.fail("parseTOML did not throw");
|
|
} catch (err) {
|
|
expect({ ...(err as Error) }).toStrictEqual({
|
|
name: "ParseError",
|
|
text: "Invalid TOML document: only letter, numbers, dashes and underscores are allowed in keys",
|
|
kind: "error",
|
|
location: {
|
|
line: 1,
|
|
column: 12,
|
|
fileText: "name = 'fail\"",
|
|
file: undefined,
|
|
lineText: "name = 'fail\"",
|
|
},
|
|
notes: [],
|
|
telemetryMessage: "TOML parse error",
|
|
});
|
|
}
|
|
});
|
|
|
|
it("should fail to parse toml with invalid header", ({ expect }) => {
|
|
try {
|
|
parseTOML(`\n[name`, "config.toml");
|
|
expect.fail("parseTOML did not throw");
|
|
} catch (err) {
|
|
expect({ ...(err as Error) }).toStrictEqual({
|
|
name: "ParseError",
|
|
text: "Invalid TOML document: incomplete key-value: cannot find end of key",
|
|
kind: "error",
|
|
location: {
|
|
line: 2,
|
|
column: 1,
|
|
file: "config.toml",
|
|
fileText: "\n[name",
|
|
lineText: "[name",
|
|
},
|
|
notes: [],
|
|
telemetryMessage: "TOML parse error",
|
|
});
|
|
}
|
|
});
|
|
|
|
it("should cope with Windows line-endings", ({ expect }) => {
|
|
expect(
|
|
parseTOML(
|
|
"# A comment with a Windows line-ending\r\n# Another comment with a Windows line-ending\r\n"
|
|
)
|
|
).toEqual({});
|
|
});
|
|
});
|
|
|
|
describe("parseJSON", () => {
|
|
it("should parse json that is empty", ({ expect }) => {
|
|
expect(parseJSON("{}")).toStrictEqual({});
|
|
});
|
|
|
|
it("should parse json with basic values", ({ expect }) => {
|
|
expect(
|
|
parseJSON(`
|
|
{
|
|
"name" : "basic",
|
|
"version": 1
|
|
}`)
|
|
).toStrictEqual({
|
|
name: "basic",
|
|
version: 1,
|
|
});
|
|
});
|
|
|
|
it("should parse json with complex values", ({ expect }) => {
|
|
expect(
|
|
parseJSON(
|
|
`{
|
|
"name":"complex",
|
|
"spec":{
|
|
"uptime":[1,2.5,3],
|
|
"ok":true
|
|
}
|
|
}`
|
|
)
|
|
).toStrictEqual({
|
|
name: "complex",
|
|
spec: {
|
|
uptime: [1, 2.5, 3],
|
|
ok: true,
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should fail to parse json with invalid string", ({ expect }) => {
|
|
try {
|
|
parseJSON(`\n{\n"version" "1\n}\n`);
|
|
expect.fail("parseJSON did not throw");
|
|
} catch (err) {
|
|
const { text, ...serialised } = err as Record<string, unknown>;
|
|
expect(serialised).toStrictEqual({
|
|
name: "ParseError",
|
|
kind: "error",
|
|
location: {
|
|
line: 3,
|
|
column: 10,
|
|
length: 2,
|
|
lineText: '"version" "1',
|
|
file: undefined,
|
|
fileText: `\n{\n"version" "1\n}\n`,
|
|
},
|
|
notes: [],
|
|
telemetryMessage: "JSON(C) parse error",
|
|
});
|
|
expect(text).toEqual("UnexpectedEndOfString");
|
|
}
|
|
});
|
|
|
|
it("should fail to parse json with invalid number", ({ expect }) => {
|
|
const file = "config.json",
|
|
fileText = `{\n\t"a":{\n\t\t"b":{\n\t\t\t"c":[012345]\n}\n}\n}`;
|
|
try {
|
|
parseJSON(fileText, file);
|
|
expect.fail("parseJSON did not throw");
|
|
} catch (err) {
|
|
expect({ ...(err as Error) }).toStrictEqual({
|
|
name: "ParseError",
|
|
text: "CommaExpected",
|
|
kind: "error",
|
|
location: {
|
|
file,
|
|
fileText,
|
|
line: 4,
|
|
column: 9,
|
|
length: 5,
|
|
lineText: `\t\t\t"c":[012345]`,
|
|
},
|
|
notes: [],
|
|
telemetryMessage: "JSON(C) parse error",
|
|
});
|
|
}
|
|
});
|
|
});
|
|
describe("parseJSONC", () => {
|
|
it("should parse jsonc that is empty", ({ expect }) => {
|
|
expect(parseJSONC("{}")).toStrictEqual({});
|
|
});
|
|
|
|
it("should parse jsonc with basic values", ({ expect }) => {
|
|
expect(
|
|
parseJSONC(`
|
|
{
|
|
"name" : "basic",
|
|
"version": 1
|
|
}`)
|
|
).toStrictEqual({
|
|
name: "basic",
|
|
version: 1,
|
|
});
|
|
});
|
|
|
|
it("should parse jsonc with complex values", ({ expect }) => {
|
|
expect(
|
|
parseJSONC(
|
|
`{
|
|
"name":"complex",
|
|
"spec":{
|
|
"uptime":[1,2.5,3],
|
|
"ok":true
|
|
}
|
|
}`
|
|
)
|
|
).toStrictEqual({
|
|
name: "complex",
|
|
spec: {
|
|
uptime: [1, 2.5, 3],
|
|
ok: true,
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should parse jsonc with comments", ({ expect }) => {
|
|
expect(
|
|
parseJSONC(
|
|
`{
|
|
// Comment A
|
|
"name":"complex",
|
|
"spec":{
|
|
// Nested comment
|
|
"uptime":[1,2.5,3],
|
|
"ok":true
|
|
}
|
|
}`
|
|
)
|
|
).toStrictEqual({
|
|
name: "complex",
|
|
spec: {
|
|
uptime: [1, 2.5, 3],
|
|
ok: true,
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should fail to parse jsonc with invalid string", ({ expect }) => {
|
|
try {
|
|
parseJSONC(`\n{\n"version" "1\n}\n`);
|
|
expect.fail("parseJSONC did not throw");
|
|
} catch (err) {
|
|
expect({ ...(err as Error) }).toStrictEqual({
|
|
name: "ParseError",
|
|
text: "UnexpectedEndOfString",
|
|
kind: "error",
|
|
location: {
|
|
length: 2,
|
|
line: 3,
|
|
column: 10,
|
|
lineText: '"version" "1',
|
|
file: undefined,
|
|
fileText: `\n{\n"version" "1\n}\n`,
|
|
},
|
|
notes: [],
|
|
telemetryMessage: "JSON(C) parse error",
|
|
});
|
|
}
|
|
});
|
|
|
|
it("should fail to parse jsonc with invalid number", ({ expect }) => {
|
|
const file = "config.json",
|
|
fileText = `{\n\t"a":{\n\t\t"b":{\n\t\t\t"c":[012345]\n}\n}\n}`;
|
|
try {
|
|
parseJSONC(fileText, file);
|
|
expect.fail("parseJSONC did not throw");
|
|
} catch (err) {
|
|
expect({ ...(err as Error) }).toStrictEqual({
|
|
name: "ParseError",
|
|
text: "CommaExpected",
|
|
kind: "error",
|
|
location: {
|
|
file,
|
|
fileText,
|
|
length: 5,
|
|
line: 4,
|
|
column: 9,
|
|
lineText: `\t\t\t"c":[012345]`,
|
|
},
|
|
notes: [],
|
|
telemetryMessage: "JSON(C) parse error",
|
|
});
|
|
}
|
|
});
|
|
});
|
|
describe("indexLocation", () => {
|
|
it("should calculate location from one-line input", ({ expect }) => {
|
|
const fileText = "";
|
|
expect(indexLocation({ fileText }, 1)).toStrictEqual({
|
|
fileText,
|
|
line: 1,
|
|
column: 0,
|
|
lineText: "",
|
|
});
|
|
});
|
|
|
|
it("should calculate location from multi-line input", ({ expect }) => {
|
|
const file = "package.json",
|
|
fileText = `\n{\n\t"hello":"world"\n}\n`;
|
|
expect(indexLocation({ file, fileText }, 11)).toStrictEqual({
|
|
file,
|
|
fileText,
|
|
line: 3,
|
|
column: 7,
|
|
lineText: `\t"hello":"world"`,
|
|
});
|
|
});
|
|
|
|
it("should calculate location when index is out of bounds", ({ expect }) => {
|
|
const fileText = `\n\n\n\n`;
|
|
expect(indexLocation({ fileText }, 10)).toStrictEqual({
|
|
fileText,
|
|
line: 5,
|
|
column: 0,
|
|
lineText: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("searchLocation", () => {
|
|
it("should calculate location from one-line match", ({ expect }) => {
|
|
const file = "config.toml",
|
|
fileText = `name = 'coolthing'`;
|
|
expect(searchLocation({ file, fileText }, "coolthing")).toStrictEqual({
|
|
file,
|
|
fileText,
|
|
line: 1,
|
|
column: 8,
|
|
length: 9,
|
|
lineText: `name = 'coolthing'`,
|
|
});
|
|
});
|
|
|
|
it("should calculate location from multi-line match", ({ expect }) => {
|
|
const fileText = `\n{"versions":[\n\t"1.2.3",\n\t"1.2.4",\n\t"1.2.5"\n]}\n`;
|
|
expect(searchLocation({ fileText }, "1.2.4")).toStrictEqual({
|
|
fileText,
|
|
line: 4,
|
|
column: 2,
|
|
length: 5,
|
|
lineText: `\t"1.2.4",`,
|
|
});
|
|
});
|
|
|
|
it("should calculate location from no match", ({ expect }) => {
|
|
const fileText = `\n{}\n`;
|
|
expect(searchLocation({ fileText }, "apple")).toStrictEqual({
|
|
fileText,
|
|
line: 3,
|
|
column: 0,
|
|
length: undefined,
|
|
lineText: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("parseByteSize", () => {
|
|
it("should calculate valid byte sizes", ({ expect }) => {
|
|
const cases: [string, number][] = [
|
|
["3", 3],
|
|
["3B", 3],
|
|
["3b", 3],
|
|
["1.3Kb", 1300],
|
|
["1.3kib", 1331],
|
|
["42MB", 42_000_000],
|
|
["42mib", 44_040_192],
|
|
["0.8MB", 800_000],
|
|
["0.8MiB", 838_860],
|
|
["2 GB", 2_000_000_000],
|
|
["2 giB", 2_147_483_648],
|
|
|
|
// If the b/ib suffix is omitted, assume non-binary units
|
|
["2G", 2_000_000_000],
|
|
["2T", 2_000_000_000_000],
|
|
["2P", 2_000_000_000_000_000],
|
|
];
|
|
|
|
for (const [input, result] of cases) {
|
|
expect(parseByteSize(input)).toEqual(result);
|
|
}
|
|
});
|
|
|
|
it("should return NaN for invalid input", ({ expect }) => {
|
|
expect(parseByteSize("")).toBeNaN();
|
|
expect(parseByteSize("foo")).toBeNaN();
|
|
expect(parseByteSize("B")).toBeNaN();
|
|
expect(parseByteSize("iB")).toBeNaN();
|
|
expect(parseByteSize(".B")).toBeNaN();
|
|
expect(parseByteSize("3iB")).toBeNaN();
|
|
expect(parseByteSize("3ib")).toBeNaN();
|
|
});
|
|
});
|