microsoft/qdk

Public

mirrored from https://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
iadavis/qdk_openqasm_parser

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

source/npm/qsharp/test/stateViz.js

159lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT license.
3
4// State visualizer snapshot tests.
5//
6// Snapshots are stored as .html files in `test/state-viz-cases/`.
7// To (re)generate snapshots:
8// node --test --test-update-snapshots test/stateViz.js
9
10// @ts-check
11
12import { JSDOM } from "jsdom";
13import fs from "node:fs";
14import path from "node:path";
15import { afterEach, beforeEach, test } from "node:test";
16import { fileURLToPath } from "node:url";
17import prettier from "prettier";
18import {
19 createStatePanel,
20 updateStatePanelFromColumns,
21} from "../dist/ux/circuit-vis/state-viz/stateViz.js";
22import { prepareStateVizColumnsFromAmpMap } from "../dist/ux/circuit-vis/state-viz/worker/stateVizPrep.js";
23
24const documentTemplate = `<!doctype html><html>
25 <head>
26 <link rel="stylesheet" href="../../ux/qsharp-ux.css">
27 <link rel="stylesheet" href="../../ux/qsharp-circuit.css">
28 </head>
29 <body></body>
30</html>`;
31
32/** @type {JSDOM | null} */
33let jsdom = null;
34
35beforeEach(() => {
36 jsdom = new JSDOM(documentTemplate, {
37 pretendToBeVisual: true,
38 });
39
40 // Override the globals used by product code
41 // @ts-expect-error - the `jsdom` typings and DOM typings don't match
42 globalThis.window = jsdom.window;
43 globalThis.document = jsdom.window.document;
44 globalThis.Node = jsdom.window.Node;
45 globalThis.HTMLElement = jsdom.window.HTMLElement;
46 globalThis.SVGElement = jsdom.window.SVGElement;
47 globalThis.SVGSVGElement = jsdom.window.SVGSVGElement;
48 globalThis.XMLSerializer = jsdom.window.XMLSerializer;
49 globalThis.getComputedStyle = jsdom.window.getComputedStyle.bind(
50 jsdom.window,
51 );
52 globalThis.requestAnimationFrame = jsdom.window.requestAnimationFrame.bind(
53 jsdom.window,
54 );
55});
56
57afterEach(() => {
58 jsdom?.window.close();
59 jsdom = null;
60});
61
62function getCasesDirectory() {
63 return path.join(
64 path.dirname(fileURLToPath(import.meta.url)),
65 "state-viz-cases",
66 );
67}
68
69/**
70 * @param {string} name
71 */
72function htmlSnapshotPath(name) {
73 // Keep snapshots stable across OSes and paths.
74 const safe = name.replace(/[^a-zA-Z0-9_.-]+/g, "_");
75 return path.join(getCasesDirectory(), safe + ".snapshot.html");
76}
77
78/**
79 * Check the current document against the stored snapshot.
80 * @param {import("node:test").TestContext} t
81 * @param {string} name
82 */
83async function checkDocumentSnapshot(t, name) {
84 const rawHtml = new XMLSerializer().serializeToString(document) + "\n";
85
86 const formattedHtml = await prettier.format(rawHtml, {
87 parser: "html",
88 printWidth: 80,
89 tabWidth: 2,
90 useTabs: false,
91 });
92
93 t.assert.fileSnapshot(formattedHtml, htmlSnapshotPath(name), {
94 serializers: [(s) => String(s)],
95 });
96}
97
98/**
99 * Creates a state panel, attaches it to the DOM, renders an amp map,
100 * and returns the panel.
101 *
102 * @param {Record<string, {re:number, im:number}>} ampMap
103 * @param {number} [maxColumns]
104 * @param {number} [minProbThreshold]
105 */
106function renderStatePanel(ampMap, maxColumns = 16, minProbThreshold = 0) {
107 const panel = createStatePanel();
108 document.body.appendChild(panel);
109
110 // For deterministic snapshots: disable animations.
111 panel.style.setProperty("--stateAnimMs", "0ms");
112
113 // Ensure panel is expanded so its contents appear in snapshots.
114 panel.classList.remove("collapsed");
115 const edge = panel.querySelector(".state-edge");
116 edge?.setAttribute("aria-expanded", "true");
117
118 const columns = prepareStateVizColumnsFromAmpMap(ampMap, {
119 maxColumns: maxColumns,
120 minProbThreshold: minProbThreshold,
121 });
122 updateStatePanelFromColumns(panel, columns, { maxColumns: maxColumns });
123 return panel;
124}
125
126test("state viz snapshot - single basis state", async (t) => {
127 renderStatePanel({
128 0: { re: 1, im: 0 },
129 });
130 await checkDocumentSnapshot(t, t.name);
131});
132
133test("state viz snapshot - superposition with phase", async (t) => {
134 const invSqrt2 = Math.SQRT1_2;
135 renderStatePanel({
136 0: { re: invSqrt2, im: 0 },
137 1: { re: 0, im: invSqrt2 }, // phase +π/2
138 });
139 await checkDocumentSnapshot(t, t.name);
140});
141
142test("state viz snapshot - threshold aggregates to Others", async (t) => {
143 renderStatePanel(
144 {
145 "00": { re: 0.94, im: 0 },
146 "01": { re: 0.2, im: 0 },
147 10: { re: 0.1, im: 0 },
148 11: { re: 0.05, im: 0 },
149 },
150 8,
151 0.05,
152 );
153 await checkDocumentSnapshot(t, t.name);
154});
155
156// Ensure the cases directory exists when running tests in fresh environments.
157if (!fs.existsSync(getCasesDirectory())) {
158 fs.mkdirSync(getCasesDirectory(), { recursive: true });
159}
160