chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,406 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user