-
Notifications
You must be signed in to change notification settings - Fork 205
Circuit-Editor and Visualization Re-Architecting and Tests #3425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ScottCarda-MS
wants to merge
28
commits into
main
Choose a base branch
from
sccarda/ce-foundation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 6 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
c2cd30d
circuit-editor foundation re-arch
ScottCarda-MS 28ff077
Added the architecture markdown file
ScottCarda-MS 384c38d
Merge branch 'main' into sccarda/ce-foundation
ScottCarda-MS 8568700
make the mermaid diagrams more readable
ScottCarda-MS f0da218
add some missing content to the Architecture file
ScottCarda-MS a086285
decided the benchmarking should be excluded, cleaned up some remainin…
ScottCarda-MS 9d26c36
consolidate data model tests
ScottCarda-MS 099257a
consolidated/removed some tests in the action/view layers
ScottCarda-MS e346b0e
dropped a couple of view layer tests
ScottCarda-MS 5af8dea
ARCHITECTURE file to replace old README
ScottCarda-MS 2bcd93d
Don't wrap comments below 100 cols
ScottCarda-MS 5739711
comments don't wrap under 100 cols in tests
ScottCarda-MS 22d93c4
condensed comment
ScottCarda-MS 5d7db8a
added some addRemove tests
ScottCarda-MS f41ddcf
added some movement tests
ScottCarda-MS 2cc4e21
reworded
ScottCarda-MS f8cae3d
remove empty out groups
ScottCarda-MS f535adb
remove unused import
ScottCarda-MS 935bc2e
Move/added some group-add tests
ScottCarda-MS d4c2059
add test for moving a doomed circuit wire
ScottCarda-MS 57c6560
more instructions about the geometry constants in the test file
ScottCarda-MS d8dd8b6
Remove local parseLocation in test.
ScottCarda-MS 10e8bea
small rename
ScottCarda-MS ca557f9
trim some tests
ScottCarda-MS 91a3f56
reflow README
ScottCarda-MS 012db0a
stop view state leak for non-editable circuits
ScottCarda-MS 37017d2
better separation of utils functions along DOM/no-DOM boundary
ScottCarda-MS 36e0331
Test to ensure view state gets removed when a new circuit is made.
ScottCarda-MS File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
218 changes: 218 additions & 0 deletions
218
source/npm/qsharp/test/circuit-editor/angleExpression.test.mjs
This file contains hidden or 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,218 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT license. | ||
|
|
||
| // angleExpression tests — direct coverage for the two helpers in | ||
| // `angleExpression.ts` that drive the Edit Argument input prompt | ||
| // in [contextMenu.ts](../../ux/circuit-vis/editor/contextMenu.ts): | ||
| // | ||
| // - `isValidAngleExpression(expr)` is the predicate the prompt | ||
| // consults on every keystroke to enable/disable OK. Anything it | ||
| // accepts ends up persisted into the operation's `args`. | ||
| // - `normalizeAngleExpression(expr)` runs BEFORE validation: it | ||
| // trims surrounding whitespace and folds case-insensitive `pi` | ||
| // to `π`. The persisted value is the normalized form, so OK's | ||
| // enabled state must agree with what the user sees after Save. | ||
|
|
||
| // @ts-check | ||
|
|
||
| import { test } from "node:test"; | ||
| import assert from "node:assert/strict"; | ||
| import { | ||
| isValidAngleExpression, | ||
| normalizeAngleExpression, | ||
| } from "../../dist/ux/circuit-vis/angleExpression.js"; | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // isValidAngleExpression — positive cases | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| test("isValidAngleExpression: plain numbers (positive, negative, decimal) are valid", () => { | ||
| // The simplest path — the prompt should accept any literal | ||
| // numeric value the user types, including the "5." trailing-dot | ||
| // form (mirroring JavaScript's `parseFloat` tolerance). | ||
| assert.equal(isValidAngleExpression("0"), true); | ||
| assert.equal(isValidAngleExpression("5"), true); | ||
| assert.equal(isValidAngleExpression("-5"), true); | ||
| assert.equal(isValidAngleExpression("+5"), true); | ||
| assert.equal(isValidAngleExpression("3.5"), true); | ||
| assert.equal(isValidAngleExpression("-3.5"), true); | ||
| assert.equal(isValidAngleExpression("5."), true); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: bare π is valid in all four case forms", () => { | ||
| // The prompt's placeholder example is `"π / 2.0"`; the user | ||
| // can type π directly via the on-screen π button, or use any | ||
| // case variant of `pi` which `normalizeAngleExpression` folds | ||
| // to π before this check runs. | ||
| assert.equal(isValidAngleExpression("π"), true); | ||
| assert.equal(isValidAngleExpression("pi"), true); | ||
| assert.equal(isValidAngleExpression("Pi"), true); | ||
| assert.equal(isValidAngleExpression("PI"), true); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: signed π is valid", () => { | ||
| // The `-π` and `+π` forms are the unary-sign-on-pi-factor path | ||
| // through the parser; pin both. | ||
| assert.equal(isValidAngleExpression("-π"), true); | ||
| assert.equal(isValidAngleExpression("+π"), true); | ||
| assert.equal(isValidAngleExpression("-pi"), true); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: arithmetic combinations are valid", () => { | ||
| // The four supported binary operators, with both numbers and π. | ||
| // These mirror the prompt's example text ("π / 2.0", "2.0 * π"). | ||
| assert.equal(isValidAngleExpression("2 + 3"), true); | ||
|
ScottCarda-MS marked this conversation as resolved.
|
||
| assert.equal(isValidAngleExpression("2 - 3"), true); | ||
| assert.equal(isValidAngleExpression("2 * 3"), true); | ||
| assert.equal(isValidAngleExpression("6 / 2"), true); | ||
| assert.equal(isValidAngleExpression("π / 2"), true); | ||
| assert.equal(isValidAngleExpression("2 * π"), true); | ||
| assert.equal(isValidAngleExpression("2.0 * π"), true); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: parentheses (including nesting) are valid", () => { | ||
| // Recursive descent through `parseFactor`'s `lpar` branch. | ||
| assert.equal(isValidAngleExpression("(π)"), true); | ||
| assert.equal(isValidAngleExpression("(2 + 3) * π"), true); | ||
|
ScottCarda-MS marked this conversation as resolved.
|
||
| assert.equal(isValidAngleExpression("((π))"), true); | ||
| assert.equal(isValidAngleExpression("π / (2 * 3)"), true); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: leading/trailing whitespace is tolerated", () => { | ||
| // `normalizeAngleExpression` (called internally by the | ||
| // evaluator) trims; the prompt also normalizes the value | ||
| // BEFORE this predicate runs, so being lenient here matches | ||
| // what the user sees after the auto-trim. | ||
| assert.equal(isValidAngleExpression(" π "), true); | ||
| assert.equal(isValidAngleExpression("\tπ / 2\n"), true); | ||
| }); | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // isValidAngleExpression — negative cases | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| test("isValidAngleExpression: empty / whitespace-only input is invalid", () => { | ||
| // The prompt's default OK-disabled state for an empty input. | ||
| // `evaluateAngleExpression` short-circuits on a falsy `expr` | ||
| // or a falsy post-normalize string. | ||
| assert.equal(isValidAngleExpression(""), false); | ||
| assert.equal(isValidAngleExpression(" "), false); | ||
| assert.equal(isValidAngleExpression("\t\n"), false); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: unknown characters are invalid", () => { | ||
| // Any character outside `[0-9.+\-*/()\sπ]` (after the `pi` → | ||
| // `π` fold) takes the tokenizer's "unknown character" fallthrough | ||
| // and returns undefined. Pin a few common typos. | ||
| assert.equal(isValidAngleExpression("π^2"), false); | ||
| assert.equal(isValidAngleExpression("sin(π)"), false); | ||
| assert.equal(isValidAngleExpression("π & 2"), false); | ||
| assert.equal(isValidAngleExpression("π,2"), false); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: malformed numbers are invalid", () => { | ||
| // The number tokenizer requires `\d+(\.\d*)?` — no leading dot, | ||
| // no multiple decimals. Both are rejected. | ||
| assert.equal( | ||
| isValidAngleExpression(".5"), | ||
| false, | ||
| "leading dot is not a valid number", | ||
| ); | ||
| assert.equal( | ||
| isValidAngleExpression("1.2.3"), | ||
| false, | ||
| "multiple decimal points are not a valid number", | ||
| ); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: unbalanced parentheses are invalid", () => { | ||
| // `parseFactor`'s `lpar` branch requires a matching `rpar` | ||
| // and returns undefined if it doesn't see one. The "extra | ||
| // rpar" case fails the trailing `k !== toks.length` guard. | ||
| assert.equal(isValidAngleExpression("(π"), false); | ||
| assert.equal(isValidAngleExpression("π)"), false); | ||
| assert.equal(isValidAngleExpression("((π)"), false); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: trailing / dangling operators are invalid", () => { | ||
| // `parseExpr` / `parseTerm` call `parseFactor` after consuming | ||
| // an operator; if the RHS factor is missing, the parse returns | ||
| // undefined. | ||
| assert.equal(isValidAngleExpression("π +"), false); | ||
| assert.equal(isValidAngleExpression("π *"), false); | ||
| assert.equal(isValidAngleExpression("2 +"), false); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: lone operators / empty parens are invalid", () => { | ||
| // No factor at all (operator only, empty parens) → parseFactor | ||
| // returns undefined on the first call. | ||
| assert.equal(isValidAngleExpression("+"), false); | ||
| assert.equal(isValidAngleExpression("-"), false); | ||
| assert.equal(isValidAngleExpression("*"), false); | ||
| assert.equal(isValidAngleExpression("()"), false); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: infinite results (division by zero) are invalid", () => { | ||
| // The evaluator's final `!isFinite(result)` guard rejects | ||
| // `1/0` and friends — the prompt should not allow the user | ||
| // to commit an angle that would evaluate to ±Infinity. | ||
| assert.equal(isValidAngleExpression("1 / 0"), false); | ||
| assert.equal(isValidAngleExpression("π / 0"), false); | ||
| assert.equal(isValidAngleExpression("-1 / 0"), false); | ||
| }); | ||
|
|
||
| test("isValidAngleExpression: adjacent factors without an operator are invalid", () => { | ||
| // Implicit multiplication isn't supported; "2π" is rejected | ||
| // even though it's a common math-notation shorthand. The | ||
| // parser leaves the `pi` token unconsumed after `parseFactor` | ||
| // returns `2`, then the trailing `k !== toks.length` guard | ||
| // trips. (If implicit-multiply support is added later, this | ||
| // test should be updated to expect `true`.) | ||
| assert.equal(isValidAngleExpression("2π"), false); | ||
| assert.equal(isValidAngleExpression("π2"), false); | ||
| }); | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // normalizeAngleExpression | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| test("normalizeAngleExpression: trims surrounding whitespace", () => { | ||
| // The prompt persists the normalized value, so leading/trailing | ||
| // whitespace must not survive into the operation's `args`. | ||
| assert.equal(normalizeAngleExpression(" π "), "π"); | ||
| assert.equal(normalizeAngleExpression("\tπ / 2\n"), "π / 2"); | ||
| assert.equal(normalizeAngleExpression(""), ""); | ||
| }); | ||
|
|
||
| test("normalizeAngleExpression: folds case-insensitive 'pi' → 'π'", () => { | ||
| // The π button on the prompt inserts the literal character, but | ||
| // users can also type any case variant of `pi`. Both must | ||
| // normalize to the same persisted form so `args` doesn't depend | ||
| // on which input path was used. | ||
| assert.equal(normalizeAngleExpression("pi"), "π"); | ||
| assert.equal(normalizeAngleExpression("Pi"), "π"); | ||
| assert.equal(normalizeAngleExpression("PI"), "π"); | ||
| assert.equal(normalizeAngleExpression("pI"), "π"); | ||
| }); | ||
|
|
||
| test("normalizeAngleExpression: folds 'pi' embedded inside an expression", () => { | ||
| // The fold is unanchored — every occurrence within an | ||
| // expression is replaced. Pin the common case (a `pi` factor | ||
| // inside an arithmetic expression). | ||
| assert.equal(normalizeAngleExpression("pi / 2"), "π / 2"); | ||
| assert.equal(normalizeAngleExpression("2 * Pi + PI"), "2 * π + π"); | ||
| assert.equal(normalizeAngleExpression("(pi)"), "(π)"); | ||
| }); | ||
|
|
||
| test("normalizeAngleExpression: leaves already-normalized π untouched", () => { | ||
| // Idempotency — passing the output back through the normalizer | ||
| // must be a no-op. The prompt re-runs the normalize step on | ||
| // every input event, so this property is what keeps the OK | ||
| // button's enabled state stable. | ||
| assert.equal(normalizeAngleExpression("π / 2"), "π / 2"); | ||
| assert.equal(normalizeAngleExpression("2 * π"), "2 * π"); | ||
| assert.equal( | ||
| normalizeAngleExpression(normalizeAngleExpression("PI / 2")), | ||
| "π / 2", | ||
| ); | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.