-
Notifications
You must be signed in to change notification settings - Fork 350
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add and pass more regression tests for PerseusItem parser (#1952)
Issue: LEMS-2582 ## Test plan: `yarn test` Author: benchristel Reviewers: jeremywiebe, benchristel Required Reviewers: Approved By: jeremywiebe Checks: ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x) Pull Request URL: #1952
- Loading branch information
1 parent
d8a714a
commit 6173771
Showing
33 changed files
with
3,744 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@khanacademy/perseus": patch | ||
--- | ||
|
||
Internal: add and pass more regression tests for PerseusItem parser |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
packages/perseus/src/util/parse-perseus-json/general-purpose-parsers/convert.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type {PartialParser} from "../parser-types"; | ||
|
||
// Given a function, creates a PartialParser that converts one type to another | ||
// using that function. The returned parser never fails. | ||
export function convert<A, B>(f: (value: A) => B): PartialParser<A, B> { | ||
return (rawValue, ctx) => ctx.success(f(rawValue)); | ||
} |
84 changes: 84 additions & 0 deletions
84
...s/perseus/src/util/parse-perseus-json/general-purpose-parsers/discriminated-union.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import {parse} from "../parse"; | ||
import {failure, success} from "../result"; | ||
|
||
import {constant} from "./constant"; | ||
import {discriminatedUnion} from "./discriminated-union"; | ||
import {number} from "./number"; | ||
import {object} from "./object"; | ||
|
||
describe("a discriminatedUnion with one variant", () => { | ||
const unionParser = discriminatedUnion( | ||
object({type: constant("ok")}), | ||
object({type: constant("ok"), value: number}), | ||
).parser; | ||
|
||
it("parses a valid value", () => { | ||
const input = {type: "ok", value: 3}; | ||
|
||
expect(parse(input, unionParser)).toEqual(success(input)); | ||
}); | ||
|
||
it("rejects a value with the wrong `type`", () => { | ||
const input = {type: "bad", value: 3}; | ||
|
||
expect(parse(input, unionParser)).toEqual( | ||
failure(`At (root).type -- expected "ok", but got "bad"`), | ||
); | ||
}); | ||
|
||
it("rejects a value with a valid type but wrong fields", () => { | ||
const input = {type: "ok", value: "foobar"}; | ||
|
||
expect(parse(input, unionParser)).toEqual( | ||
failure(`At (root).value -- expected number, but got "foobar"`), | ||
); | ||
}); | ||
}); | ||
|
||
describe("a discriminatedUnion with two variants", () => { | ||
const unionParser = discriminatedUnion( | ||
object({type: constant("rectangle")}), | ||
object({type: constant("rectangle"), width: number}), | ||
).or( | ||
object({type: constant("circle")}), | ||
object({type: constant("circle"), radius: number}), | ||
).parser; | ||
|
||
it("parses a valid rectangle", () => { | ||
const input = {type: "rectangle", width: 42}; | ||
|
||
expect(parse(input, unionParser)).toEqual(success(input)); | ||
}); | ||
|
||
it("rejects a rectangle with no width", () => { | ||
const input = {type: "rectangle", radius: 99}; | ||
|
||
expect(parse(input, unionParser)).toEqual( | ||
failure(`At (root).width -- expected number, but got undefined`), | ||
); | ||
}); | ||
|
||
it("parses a valid circle", () => { | ||
const input = {type: "circle", radius: 7}; | ||
|
||
expect(parse(input, unionParser)).toEqual(success(input)); | ||
}); | ||
|
||
it("rejects a circle with no radius", () => { | ||
const input = {type: "circle", width: 99}; | ||
|
||
expect(parse(input, unionParser)).toEqual( | ||
failure(`At (root).radius -- expected number, but got undefined`), | ||
); | ||
}); | ||
|
||
it("rejects a value with an unrecognized `type`", () => { | ||
const input = {type: "triangle", width: -1, radius: 99}; | ||
|
||
expect(parse(input, unionParser)).toEqual( | ||
failure( | ||
`At (root).type -- expected "rectangle", but got "triangle"`, | ||
), | ||
); | ||
}); | ||
}); |
46 changes: 46 additions & 0 deletions
46
packages/perseus/src/util/parse-perseus-json/general-purpose-parsers/discriminated-union.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {isSuccess} from "../result"; | ||
|
||
import {pipeParsers} from "./pipe-parsers"; | ||
|
||
import type {Parser} from "../parser-types"; | ||
|
||
// discriminatedUnion() should be preferred over union() when parsing a | ||
// discriminated union type, because discriminatedUnion() produces more | ||
// understandable failure messages. It takes the discriminant as the source of | ||
// truth for which variant is to be parsed, and expects the other data to match | ||
// that variant. | ||
export function discriminatedUnion<T>( | ||
narrow: Parser<unknown>, | ||
parseVariant: Parser<T>, | ||
): DiscriminatedUnionBuilder<T> { | ||
return new DiscriminatedUnionBuilder( | ||
pipeParsers(narrow).then(parseVariant).parser, | ||
); | ||
} | ||
|
||
class DiscriminatedUnionBuilder<Variant> { | ||
constructor(public parser: Parser<Variant>) {} | ||
|
||
or<Variant2>( | ||
narrow: Parser<unknown>, | ||
parseVariant: Parser<Variant2>, | ||
): DiscriminatedUnionBuilder<Variant | Variant2> { | ||
return new DiscriminatedUnionBuilder( | ||
either(narrow, parseVariant, this.parser), | ||
); | ||
} | ||
} | ||
|
||
function either<A, B>( | ||
narrowToA: Parser<unknown>, | ||
parseA: Parser<A>, | ||
parseB: Parser<B>, | ||
): Parser<A | B> { | ||
return (rawValue, ctx) => { | ||
if (isSuccess(narrowToA(rawValue, ctx))) { | ||
return parseA(rawValue, ctx); | ||
} | ||
|
||
return parseB(rawValue, ctx); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.