Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
67e7300
expose circuit-to-qsharp function to TS
ScottCarda-MS May 7, 2026
794983c
circuit code preview
ScottCarda-MS May 7, 2026
2a654ce
Show read-only live code panel
ScottCarda-MS May 7, 2026
24aba36
Clean up and command for showing preview
ScottCarda-MS May 7, 2026
23fc1cd
deep emitter with simple loop and cond handling
ScottCarda-MS May 8, 2026
790007a
add divergence banner
ScottCarda-MS May 8, 2026
14c7508
bridge to export from circuit visual
ScottCarda-MS May 8, 2026
0611355
Fix some cod view errors from Q# generation
ScottCarda-MS May 9, 2026
8b0fb07
Major CE internal re-architecting
ScottCarda-MS May 9, 2026
136472c
move panel.ts
ScottCarda-MS May 11, 2026
7475a74
Run-button is only made when available
ScottCarda-MS May 11, 2026
1bce416
Split up panel.ts
ScottCarda-MS May 13, 2026
e53d708
Split up panel.ts
ScottCarda-MS May 13, 2026
1caa7c4
Updated comment descriptions
ScottCarda-MS May 13, 2026
d2d0ceb
Updating comment descriptions
ScottCarda-MS May 13, 2026
27c3100
Added additional tests
ScottCarda-MS May 13, 2026
46f41cd
fix inconsistent out-of-bounds handling
ScottCarda-MS May 13, 2026
1998064
Move CE tests into subfolder
ScottCarda-MS May 13, 2026
23d0601
Move state-viz tests to match package structure
ScottCarda-MS May 13, 2026
59819e6
run-button tests
ScottCarda-MS May 13, 2026
2c587ef
Add Architecture markdown file
ScottCarda-MS May 13, 2026
df1ba15
Move controllers into subfolder for organization
ScottCarda-MS May 13, 2026
42be44b
Fix dropzones not rendering in nested groups
ScottCarda-MS May 13, 2026
19d17eb
view-state and persistent expand/collapse group
ScottCarda-MS May 13, 2026
518fe2f
Fix undo/redo behavior for persisting view state
ScottCarda-MS May 14, 2026
7664ca1
update to TODO
ScottCarda-MS May 14, 2026
1b24f63
Temp Remove LCP and export features
ScottCarda-MS May 15, 2026
f4f0c09
Fix some dragging bugs
ScottCarda-MS May 18, 2026
a3fb3dc
WIP trying to support multi-target gate drag-n-drop
ScottCarda-MS May 18, 2026
151928b
Prevent conditioned gates from moving before their dependent measures.
ScottCarda-MS May 19, 2026
637ebd5
Move multi-target based by shift based on nearest wire
ScottCarda-MS May 19, 2026
c9e7da6
Updated plan for D4 group behavior
ScottCarda-MS May 19, 2026
ed75a89
right-trailing dropzone column for expanding groups to the right.
ScottCarda-MS May 20, 2026
0d675a0
update TODO
ScottCarda-MS May 20, 2026
2bac73f
Added vertical extend group with shift drag
ScottCarda-MS May 21, 2026
e6cd68f
Update ancestors' targets when updating children.
ScottCarda-MS May 26, 2026
0a27c25
Roadmap file
ScottCarda-MS May 27, 2026
99c4634
Addressed a lot of issues with classical control gates. Found a lot o…
ScottCarda-MS May 27, 2026
77b11f4
Merge branch 'main' into sccarda/CErearchitecting
ScottCarda-MS May 28, 2026
8274fef
Fix null classical control indicators
ScottCarda-MS May 28, 2026
6aa5f49
Groups keep track of children during qubit move and clone group ops
ScottCarda-MS May 28, 2026
85fd459
WIP support for controls on groups
ScottCarda-MS May 29, 2026
6e5dc82
Fix vertical neighbors not moving out of the way on group expand
ScottCarda-MS May 30, 2026
742f102
moving vertical neighbors out of the way
ScottCarda-MS Jun 1, 2026
247452c
FIx bug with moving M on qubit with multiple Ms with dependencies
ScottCarda-MS Jun 1, 2026
186171f
update roadmap
ScottCarda-MS Jun 1, 2026
3b72787
fix bug with moving controls
ScottCarda-MS Jun 2, 2026
8ca8d60
Let's just prevent control on groups for now
ScottCarda-MS Jun 2, 2026
e383c46
disallow adjoint on gates with children
ScottCarda-MS Jun 3, 2026
595b9ac
Test coverage plan
ScottCarda-MS Jun 3, 2026
c3dfbdc
tests
ScottCarda-MS Jun 5, 2026
a557326
more tests
ScottCarda-MS Jun 5, 2026
f2b3b6c
Clean up test comments
ScottCarda-MS Jun 5, 2026
83e95d9
add some organization to tests
ScottCarda-MS Jun 8, 2026
8f54717
fix typing errors in test files
ScottCarda-MS Jun 8, 2026
9924231
small changes to some of the tests
ScottCarda-MS Jun 8, 2026
9503254
nervously making a change to the utils getWireRange
ScottCarda-MS Jun 9, 2026
dbec28d
copied out a subsection of circuit action tests for just add/remove o…
ScottCarda-MS Jun 9, 2026
69af789
Move a couple more tests into the addRemove file
ScottCarda-MS Jun 10, 2026
e36545a
Break circuit actions tests into sub-files
ScottCarda-MS Jun 10, 2026
49fa274
Clean up circuit action group test and added some helpers to make rea…
ScottCarda-MS Jun 16, 2026
d9f308e
group move tests
ScottCarda-MS Jun 16, 2026
acf0bdb
clean up other circuit action tests
ScottCarda-MS Jun 16, 2026
81e8256
remove old circuit action test files
ScottCarda-MS Jun 16, 2026
4074bfe
Bit more progress on test audit
ScottCarda-MS Jun 17, 2026
a07e694
dropzone.tests.mjs audit
ScottCarda-MS Jun 17, 2026
5a6c1f9
more test audits
ScottCarda-MS Jun 17, 2026
43a5bdd
toolbox test audit
ScottCarda-MS Jun 17, 2026
18ca456
drag controller tests
ScottCarda-MS Jun 17, 2026
7f80616
Finished controller test audit
ScottCarda-MS Jun 17, 2026
a7e998b
sqore test audi
ScottCarda-MS Jun 17, 2026
43c0450
update roadmap and todo files
ScottCarda-MS Jun 17, 2026
479d87c
updated the wording of some comments
ScottCarda-MS Jun 18, 2026
ce7c892
Cleaned up data/ comments
ScottCarda-MS Jun 29, 2026
6f81805
some more comment cleanup
ScottCarda-MS Jun 29, 2026
80a2f42
split circuitActions.ts into several files
ScottCarda-MS Jun 29, 2026
ae7f3be
update helper md files
ScottCarda-MS Jun 29, 2026
a407f10
trimmed comments of circuit action files
ScottCarda-MS Jun 29, 2026
5447b21
trim comments in circuit actions
ScottCarda-MS Jun 30, 2026
3dc4d63
findAndRemoveOperations is now private
ScottCarda-MS Jun 30, 2026
2d02e10
minor adjustment to getMinMaxRegIdx
ScottCarda-MS Jun 30, 2026
2bf4bc2
make dropzone creation more manageable
ScottCarda-MS Jun 30, 2026
aac1f2e
remove broken link syntax in comments
ScottCarda-MS Jun 30, 2026
e3b99e8
trim comments under editor/
ScottCarda-MS Jun 30, 2026
93cf654
reorg prompts
ScottCarda-MS Jul 1, 2026
ca129a4
move more prompt logic into prompts.ts
ScottCarda-MS Jul 1, 2026
c8df7d2
trims up the comments in the circuit-vis/ folder
ScottCarda-MS Jul 1, 2026
646fcb7
Remove CE planning docs from tracked tree (kept local-only)
ScottCarda-MS Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
541 changes: 541 additions & 0 deletions source/npm/qsharp/test/circuit-editor/_helpers.mjs

Large diffs are not rendered by default.

218 changes: 218 additions & 0 deletions source/npm/qsharp/test/circuit-editor/angleExpression.test.mjs
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);
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);
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",
);
});
Loading
Loading