Skip to content

Commit 89aa703

Browse files
authored
Add more UI widget tests for ipywidgets (microsoft#10735)
* Add more tests * ipyvolume and pythreejs tests
1 parent 239f516 commit 89aa703

6 files changed

Lines changed: 675 additions & 2 deletions

File tree

build/conda-functional-requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ flask
1717
django
1818
isort
1919
pathlib2>=2.2.0 ; python_version<'3.6' # Python 2.7 compatibility (pytest)
20+
pythreejs
21+
ipyvolume

src/test/datascience/uiTests/helpers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const maxWaitTimeForMessage = 15_000;
3838
export const waitTimeForUIToUpdate = 3_000;
3939

4040
export class BaseWebUI implements IAsyncDisposable {
41-
protected page?: playwright.Page;
41+
public page?: playwright.Page;
4242
private readonly disposables: IDisposable[] = [];
4343
private readonly webServerPromise = createDeferred<WebServer>();
4444
private webServer?: WebServer;
@@ -50,6 +50,9 @@ export class BaseWebUI implements IAsyncDisposable {
5050
await this.browser?.close();
5151
await this.page?.close();
5252
}
53+
public async type(text: string): Promise<void> {
54+
await this.page?.keyboard.type(text);
55+
}
5356
public _setWebServer(webServer: WebServer) {
5457
this.webServer = webServer;
5558
this.webServerPromise.resolve(webServer);

src/test/datascience/uiTests/nativeEditor.ui.functional.test.ts

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ use(chaiAsPromised);
8585
if (nb.metadata && nb.metadata.kernelspec) {
8686
delete nb.metadata.kernelspec;
8787
}
88+
// Clear all output (from previous executions).
89+
nb.cells.forEach(cell => {
90+
if (Array.isArray(cell.outputs)) {
91+
cell.outputs = [];
92+
}
93+
});
8894
const result = await openNotebook(ioc, disposables, JSON.stringify(nb));
8995
notebookUi = result.notebookUI;
9096
return result;
@@ -98,6 +104,15 @@ use(chaiAsPromised);
98104
async function openStandardWidgetsIpynb() {
99105
return openNotebookFile('standard_widgets.ipynb');
100106
}
107+
async function openIPySheetsIpynb() {
108+
return openNotebookFile('ipySheet_widgets.ipynb');
109+
}
110+
async function openIPyVolumeIpynb() {
111+
return openNotebookFile('ipyvolume_widgets.ipynb');
112+
}
113+
async function openPyThreejsIpynb() {
114+
return openNotebookFile('pythreejs_widgets.ipynb');
115+
}
101116

102117
test('Notebook has 3 cells', async () => {
103118
const { notebookUI } = await openABCIpynb();
@@ -192,7 +207,7 @@ use(chaiAsPromised);
192207

193208
await notebookUI.executeCell(3);
194209
await notebookUI.executeCell(4);
195-
// await sleep(500_000);
210+
196211
const button = await retryIfFail(async () => {
197212
// Find the button & the lable in cell output for 3 & 4 respectively.
198213
const buttons = await (await notebookUI.getCellOutput(3)).$$('button.widget-button');
@@ -212,6 +227,188 @@ use(chaiAsPromised);
212227
assert.include(cell4Output, 'Button Clicked');
213228
});
214229
});
230+
test('Render ipysheets', async () => {
231+
const { notebookUI } = await openIPySheetsIpynb();
232+
await assert.eventually.isFalse(notebookUI.cellHasOutput(3));
233+
234+
await notebookUI.executeCell(1);
235+
await notebookUI.executeCell(3);
236+
237+
await retryIfFail(async () => {
238+
const cellOutput = await notebookUI.getCellOutputHTML(3);
239+
240+
assert.include(cellOutput, 'Hello</td>');
241+
assert.include(cellOutput, 'World</td>');
242+
});
243+
});
244+
test('Render ipysheets', async () => {
245+
const { notebookUI } = await openIPySheetsIpynb();
246+
await assert.eventually.isFalse(notebookUI.cellHasOutput(3));
247+
248+
await notebookUI.executeCell(1);
249+
await notebookUI.executeCell(3);
250+
251+
await retryIfFail(async () => {
252+
const cellOutput = await notebookUI.getCellOutputHTML(3);
253+
254+
assert.include(cellOutput, 'Hello</td>');
255+
assert.include(cellOutput, 'World</td>');
256+
});
257+
});
258+
test('Search ipysheets with textbox in another cell', async () => {
259+
const { notebookUI } = await openIPySheetsIpynb();
260+
await assert.eventually.isFalse(notebookUI.cellHasOutput(6));
261+
await assert.eventually.isFalse(notebookUI.cellHasOutput(7));
262+
263+
await notebookUI.executeCell(5);
264+
await notebookUI.executeCell(6);
265+
await notebookUI.executeCell(7);
266+
267+
// Wait for sheets to get rendered.
268+
await retryIfFail(async () => {
269+
const cellOutputHtml = await notebookUI.getCellOutputHTML(7);
270+
271+
assert.include(cellOutputHtml, 'test</td>');
272+
assert.include(cellOutputHtml, 'train</td>');
273+
274+
const cellOutput = await notebookUI.getCellOutput(6);
275+
const highlighted = await cellOutput.$$('td.htSearchResult');
276+
assert.equal(highlighted.length, 0);
277+
});
278+
279+
// Type `test` into textbox.
280+
await retryIfFail(async () => {
281+
const cellOutput = await notebookUI.getCellOutput(6);
282+
const textboxes = await cellOutput.$$('input[type=text]');
283+
assert.equal(textboxes.length, 1, 'No Texbox');
284+
await textboxes[0].focus();
285+
286+
await notebookUI.type('test');
287+
});
288+
289+
// Confirm cell is filtered and highlighted.
290+
await retryIfFail(async () => {
291+
const cellOutput = await notebookUI.getCellOutput(7);
292+
const highlighted = await cellOutput.$$('td.htSearchResult');
293+
assert.equal(highlighted.length, 2);
294+
});
295+
});
296+
test('Update ipysheets cells with textbox & slider in another cell', async () => {
297+
const { notebookUI } = await openIPySheetsIpynb();
298+
await assert.eventually.isFalse(notebookUI.cellHasOutput(10));
299+
await assert.eventually.isFalse(notebookUI.cellHasOutput(12));
300+
await assert.eventually.isFalse(notebookUI.cellHasOutput(13));
301+
302+
await notebookUI.executeCell(9);
303+
await notebookUI.executeCell(10);
304+
await notebookUI.executeCell(12);
305+
await notebookUI.executeCell(13);
306+
307+
// Wait for slider to get rendered with value `0`.
308+
const sliderLabel = await retryIfFail(async () => {
309+
const cellOutputHtml = await notebookUI.getCellOutputHTML(10);
310+
311+
assert.include(cellOutputHtml, 'ui-slider-handle');
312+
assert.include(cellOutputHtml, 'left: 0%');
313+
314+
const cellOutput = await notebookUI.getCellOutput(10);
315+
const sliderLables = await cellOutput.$$('div.widget-readout');
316+
317+
return sliderLables[0];
318+
});
319+
320+
// Confirm slider lable reads `0`.
321+
await retryIfFail(async () => {
322+
const sliderValue = await notebookUI.page?.evaluate(ele => ele.innerHTML.trim(), sliderLabel);
323+
assert.equal(sliderValue || '', '0');
324+
});
325+
326+
// Wait for textbox to get rendered.
327+
const textbox = await retryIfFail(async () => {
328+
const cellOutput = await notebookUI.getCellOutput(12);
329+
const textboxes = await cellOutput.$$('input[type=number]');
330+
assert.equal(textboxes.length, 1);
331+
332+
const value = await notebookUI.page?.evaluate(el => (el as HTMLInputElement).value, textboxes[0]);
333+
assert.equal(value || '', '0');
334+
335+
return textboxes[0];
336+
});
337+
338+
// Wait for sheets to get rendered.
339+
await retryIfFail(async () => {
340+
const cellOutputHtml = await notebookUI.getCellOutputHTML(13);
341+
assert.include(cellOutputHtml, '>50.000</td>');
342+
assert.notInclude(cellOutputHtml, '>100.000</td>');
343+
});
344+
345+
// Type `50` into textbox.
346+
await retryIfFail(async () => {
347+
await textbox.focus();
348+
await notebookUI.type('50');
349+
});
350+
351+
// Confirm slider label reads `50`.
352+
await retryIfFail(async () => {
353+
const sliderValue = await notebookUI.page?.evaluate(ele => ele.innerHTML.trim(), sliderLabel);
354+
assert.equal(sliderValue || '', '50');
355+
});
356+
357+
// Wait for sheets to get updated with calculation.
358+
await retryIfFail(async () => {
359+
const cellOutputHtml = await notebookUI.getCellOutputHTML(13);
360+
361+
assert.include(cellOutputHtml, '>50.000</td>');
362+
assert.include(cellOutputHtml, '>100.000</td>');
363+
});
364+
});
365+
test('Render ipyvolume', async () => {
366+
const { notebookUI } = await openIPyVolumeIpynb();
367+
await assert.eventually.isFalse(notebookUI.cellHasOutput(3));
368+
369+
await notebookUI.executeCell(1);
370+
await notebookUI.executeCell(2);
371+
await notebookUI.executeCell(3);
372+
await notebookUI.executeCell(4);
373+
374+
// Confirm sliders and canvas are rendered.
375+
await retryIfFail(async () => {
376+
const cellOutputHtml = await notebookUI.getCellOutputHTML(1);
377+
assert.include(cellOutputHtml, '<canvas ');
378+
379+
const cellOutput = await notebookUI.getCellOutput(1);
380+
const sliders = await cellOutput.$$('div.ui-slider');
381+
assert.equal(sliders.length, 2);
382+
});
383+
384+
// Confirm canvas is rendered.
385+
await retryIfFail(async () => {
386+
const cellOutputHtml = await notebookUI.getCellOutputHTML(4);
387+
assert.include(cellOutputHtml, '<canvas ');
388+
});
389+
});
390+
test('Render pythreejs', async () => {
391+
const { notebookUI } = await openPyThreejsIpynb();
392+
await assert.eventually.isFalse(notebookUI.cellHasOutput(3));
393+
await assert.eventually.isFalse(notebookUI.cellHasOutput(8));
394+
395+
await notebookUI.executeCell(1);
396+
await notebookUI.executeCell(2);
397+
await notebookUI.executeCell(3);
398+
await notebookUI.executeCell(4);
399+
await notebookUI.executeCell(5);
400+
await notebookUI.executeCell(6);
401+
await notebookUI.executeCell(7);
402+
await notebookUI.executeCell(8);
403+
404+
// Confirm canvas is rendered.
405+
await retryIfFail(async () => {
406+
let cellOutputHtml = await notebookUI.getCellOutputHTML(3);
407+
assert.include(cellOutputHtml, '<canvas ');
408+
cellOutputHtml = await notebookUI.getCellOutputHTML(8);
409+
assert.include(cellOutputHtml, '<canvas ');
410+
});
411+
});
215412
});
216413
});
217414
});

0 commit comments

Comments
 (0)