Skip to content

Commit ad5cc47

Browse files
Migrate JavaScript unit tests to Playwright (AcademySoftwareFoundation#2952)
This changelist migrates the JavaScript unit tests from Mocha and Chai to Playwright, harmonizing on a single JavaScript test framework for both browser and unit tests. The following specific changes are included: - Convert the unit tests from Mocha and Chai to Playwright, removing the `mocha` and `chai` dependencies. - Run the Node and browser tests as separate Playwright projects from a single configuration. - Stage the WASM build artifacts through a Playwright `globalSetup` rather than npm `pretest` hooks. - Restore assertions that had silently become no-ops, so each `expect` again verifies its condition. - Fix an inverted `readXIncludes` getter on `XmlReadOptions`, uncovered by the restored assertions.
1 parent 9b2eb05 commit ad5cc47

17 files changed

Lines changed: 858 additions & 1880 deletions

javascript/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ build
22
**/_build
33
**/dist
44
**/node_modules
5+
**/playwright-report
Lines changed: 89 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,90 @@
1-
import { expect } from 'chai';
1+
import { test, expect } from '@playwright/test';
22
import Module from './_build/JsMaterialXCore.js';
33
import { getMtlxStrings } from './testHelpers.js';
44

5-
describe('Code Examples', () =>
5+
test.describe('Code Examples', () =>
66
{
7-
it('Building a MaterialX Document', async () =>
7+
test('Building a MaterialX Document', async () =>
88
{
99
const mx = await Module();
1010
// Create a document.
1111
const doc = mx.createDocument();
1212

1313
// Create a node graph with a single image node and output.
1414
const nodeGraph = doc.addNodeGraph();
15-
expect(doc.getNodeGraphs().length).to.equal(1);
15+
expect(doc.getNodeGraphs().length).toBe(1);
1616
const image = nodeGraph.addNode('image');
1717
const nodes = nodeGraph.getNodes();
18-
expect(nodes.length).to.equal(1);
19-
expect(nodes[0]).to.eql(image);
18+
expect(nodes.length).toBe(1);
19+
expect(nodes[0].equals(image)).toBe(true);
2020

2121
image.setInputValueString('file', 'image1.tif', 'filename');
2222
const input = image.getInput('file');
23-
expect(input).to.not.be.null;
24-
expect(input.getValue().getData()).to.equal('image1.tif');
23+
expect(input).not.toBeNull();
24+
expect(input.getValue().getData()).toBe('image1.tif');
2525

2626
const output = nodeGraph.addOutput();
2727
const outputs = nodeGraph.getOutputs();
28-
expect(outputs.length).to.equal(1);
29-
expect(outputs[0]).to.eql(output);
28+
expect(outputs.length).toBe(1);
29+
expect(outputs[0].equals(output)).toBe(true);
3030

3131
output.setConnectedNode(image);
3232
const connectedNode = output.getConnectedNode();
33-
expect(connectedNode).to.not.be.null;
34-
expect(connectedNode instanceof mx.Node).to.be.true;
33+
expect(connectedNode).not.toBeNull();
34+
expect(connectedNode instanceof mx.Node).toBe(true);
3535

3636
// Create a simple shader interface.
3737
const simpleSrf = doc.addNodeDef('ND_simpleSrf', 'surfaceshader', 'simpleSrf');
3838
const nodeDefs = doc.getNodeDefs();
39-
expect(nodeDefs.length).to.equal(1);
40-
expect(nodeDefs[0]).to.eql(simpleSrf);
39+
expect(nodeDefs.length).toBe(1);
40+
expect(nodeDefs[0].equals(simpleSrf)).toBe(true);
4141

4242
simpleSrf.setInputValueColor3('diffColor', new mx.Color3(1.0, 1.0, 1.0));
4343
let inputValue = simpleSrf.getInputValue('diffColor');
44-
expect(inputValue).to.not.be.null;
45-
expect(inputValue.getData()).to.eql(new mx.Color3(1.0, 1.0, 1.0));
44+
expect(inputValue).not.toBeNull();
45+
expect(inputValue.getData().equals(new mx.Color3(1.0, 1.0, 1.0))).toBe(true);
4646

4747
simpleSrf.setInputValueColor3('specColor', new mx.Color3(0.0, 0.0, 0.0));
4848
inputValue = simpleSrf.getInputValue('specColor');
49-
expect(inputValue).to.not.be.null;
50-
expect(inputValue.getData()).to.eql(new mx.Color3(0.0, 0.0, 0.0));
49+
expect(inputValue).not.toBeNull();
50+
expect(inputValue.getData().equals(new mx.Color3(0.0, 0.0, 0.0))).toBe(true);
5151

5252
const roughness = simpleSrf.setInputValueFloat('roughness', 0.25);
5353
inputValue = simpleSrf.getInputValue('roughness');
54-
expect(inputValue).to.not.be.null;
55-
expect(inputValue.getData()).to.equal(0.25);
56-
57-
// // Create a material that instantiates the shader.
58-
// const material = doc.addMaterial();
59-
// const materials = doc.getMaterials();
60-
// expect(materials.length).to.equal(1);
61-
// expect(materials[0]).to.eql(material);
62-
// const refSimpleSrf = material.addShaderRef('SR_simpleSrf', 'simpleSrf');
63-
// const shaderRefs = material.getShaderRefs();
64-
// expect(shaderRefs.length).to.equal(1);
65-
// expect(shaderRefs[0]).to.eql(refSimpleSrf);
66-
// expect(shaderRefs[0].getName()).to.equal('SR_simpleSrf');
67-
68-
// // Bind roughness to a new value within this material.
69-
// const bindInput = refSimpleSrf.addBindInput('roughness');
70-
// const bindInputs = refSimpleSrf.getBindInputs();
71-
// expect(bindInputs.length).to.equal(1);
72-
// expect(bindInputs[0]).to.eql(bindInput);
73-
// bindInput.setValuefloat(0.5);
74-
// expect(bindInput.getValue()).to.not.be.null;
75-
// expect(bindInput.getValue().getData()).to.equal(0.5);
76-
77-
// // Validate the value of roughness in the context of this material.
78-
// expect(roughness.getBoundValue(material).getValueString()).to.equal('0.5');
54+
expect(inputValue).not.toBeNull();
55+
expect(inputValue.getData()).toBe(0.25);
56+
57+
// Instantiate the shader and create a material node that references it.
58+
const shaderNode = doc.addNodeInstance(simpleSrf);
59+
const materialNode = doc.addMaterialNode('', shaderNode);
60+
expect(doc.getMaterialNodes().length).toBe(1);
61+
62+
// Connect the diffuse color of the shader to the image output, and
63+
// confirm that the upstream image node is reachable from the shader.
64+
shaderNode.setConnectedOutput('diffColor', output);
65+
const upstreamNode = shaderNode.getUpstreamElement();
66+
expect(upstreamNode).not.toBeNull();
67+
expect(upstreamNode.getName()).toBe(image.getName());
68+
69+
// Override roughness on this shader instance; its default value is
70+
// inherited from the shader's nodedef.
71+
const instanceRoughness = shaderNode.setInputValueFloat('roughness', 0.5);
72+
expect(instanceRoughness.getValue().getData()).toBe(0.5);
73+
expect(instanceRoughness.getDefaultValue().getData()).toBe(roughness.getValue().getData());
74+
7975
// Cleanup wrappers
8076
nodeDefs.forEach(nd => nd.delete());
77+
upstreamNode.delete();
78+
instanceRoughness.delete();
79+
materialNode.delete();
80+
shaderNode.delete();
8181
output.delete();
8282
image.delete();
8383
nodeGraph.delete();
8484
doc.delete();
8585
});
8686

87-
it('Traversing a Document Tree', async () =>
87+
test('Traversing a Document Tree', async () =>
8888
{
8989
const xmlStr = getMtlxStrings(
9090
['standard_surface_greysphere_calibration.mtlx'],
@@ -106,11 +106,11 @@ describe('Code Examples', () =>
106106
imageCount++;
107107
}
108108
}
109-
expect(imageCount).to.greaterThan(0);
109+
expect(imageCount).toBeGreaterThan(0);
110110
doc.delete();
111111
});
112112

113-
it('Building a MaterialX Document', async () =>
113+
test('Traversing a Dataflow Graph', async () =>
114114
{
115115
const xmlStr = getMtlxStrings(['standard_surface_marble_solid.mtlx'], '../../resources/Materials/Examples/StandardSurface')[0];
116116
const mx = await Module();
@@ -119,27 +119,49 @@ describe('Code Examples', () =>
119119
const doc = mx.createDocument();
120120
await mx.readFromXmlString(doc, xmlStr);
121121

122-
// let materialCount = 0;
123-
// let shaderInputCount = 0;
124-
// // Iterate through 1.37 materials for which there should be none
125-
// const materials = doc.getMaterials();
126-
// materials.forEach((material) => {
127-
// materialCount++;
128-
129-
// // For each shader input, find all upstream images in the dataflow graph.
130-
// const primaryShaderInputs = material.getPrimaryShaderInputs();
131-
// primaryShaderInputs.forEach((input) => {
132-
// const graphIter = input.traverseGraph(material);
133-
// let edge = graphIter.next();
134-
// while (edge) {
135-
// shaderInputCount++;
136-
// edge = graphIter.next();
137-
// }
138-
// });
139-
// });
140-
141-
// expect(materialCount).to.equal(0);
142-
// expect(shaderInputCount).to.equal(0);
122+
// For each material node, locate its surface shader and traverse the
123+
// dataflow graph upstream, gathering the nodes that contribute to the
124+
// shading result. The marble surface is driven by a procedural noise
125+
// network bound through a nodegraph, so the traversal crosses from the
126+
// top-level document into that nodegraph.
127+
const materialNodes = doc.getMaterialNodes();
128+
expect(materialNodes.length).toBe(1);
129+
130+
let nodeCount = 0;
131+
let crossedIntoNodeGraph = false;
132+
for (const materialNode of materialNodes)
133+
{
134+
const shaderNodes = mx.getShaderNodes(materialNode);
135+
for (const shaderNode of shaderNodes)
136+
{
137+
for (const edge of shaderNode.traverseGraph())
138+
{
139+
const upstreamElem = edge.getUpstreamElement();
140+
if (upstreamElem instanceof mx.Node)
141+
{
142+
nodeCount++;
143+
144+
// An upstream node whose parent is a nodegraph rather
145+
// than the document confirms that the traversal has
146+
// descended into the bound nodegraph.
147+
const parent = upstreamElem.getParent();
148+
if (parent instanceof mx.NodeGraph)
149+
{
150+
crossedIntoNodeGraph = true;
151+
}
152+
if (parent) parent.delete();
153+
}
154+
if (upstreamElem) upstreamElem.delete();
155+
if (edge) edge.delete();
156+
}
157+
}
158+
shaderNodes.forEach(s => s.delete());
159+
}
160+
expect(nodeCount).toBeGreaterThan(0);
161+
expect(crossedIntoNodeGraph).toBe(true);
162+
163+
// Cleanup wrappers
164+
materialNodes.forEach(n => n.delete());
143165
doc.delete();
144166
});
145167
});

0 commit comments

Comments
 (0)