Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 12 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
- ':nbrowser-^[M-N]:'
- ':nbrowser-^[O-R]:'
- ':nbrowser-^[^A-R]:'
- ':projects:'
include:
- tests: ':lint:python:client:common:smoke:'
node-version: 22.x
Expand Down Expand Up @@ -84,7 +85,7 @@ jobs:
run: yarn run build

- name: Install Google Chrome and chromedriver
if: contains(matrix.tests, ':nbrowser-') || contains(matrix.tests, ':smoke:') || contains(matrix.tests, ':stubs:')
if: contains(matrix.tests, ':nbrowser-') || contains(matrix.tests, ':smoke:') || contains(matrix.tests, ':stubs:') || contains(matrix.tests, ':projects:')
run: buildtools/install_chrome_for_tests.sh -y

- name: Run smoke test
Expand Down Expand Up @@ -195,6 +196,16 @@ jobs:
GVISOR_EXTRA_DIRS: /opt
TESTDIR: ${{ runner.temp }}/test-logs

- name: Run projects tests
if: contains(matrix.tests, ':projects:')
run: |
mkdir -p $MOCHA_WEBDRIVER_LOGDIR
yarn run test:projects
env:
MOCHA_WEBDRIVER_LOGDIR: ${{ runner.temp }}/test-logs/webdriver
TESTDIR: ${{ runner.temp }}/test-logs
MOCHA_WEBDRIVER_HEADLESS: 1

- name: Prepare for saving artifact
if: failure()
run: |
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@
"ts-node": "10.9.2",
"typescript": "4.7.4",
"webpack": "5.97.1",
"webpack-cli": "4.10.0",
"webpack-dev-server": "5.2.1",
"webpack-cli": "6.0.1",
"webpack-dev-server": "5.2.2",
"why-is-node-running": "2.2.2"
},
"dependencies": {
Expand Down
17 changes: 12 additions & 5 deletions test/fixtures/projects/webpack-test-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {exitPromise} from 'app/server/lib/serverUtils';
import {ChildProcess, spawn} from 'child_process';
import {driver, IMochaContext, IMochaServer} from 'mocha-webdriver';
import fetch from 'node-fetch';
import stripAnsi from "strip-ansi";
import * as path from 'path';

const configPath = process.env.PROJECTS_WEBPACK_CONFIG || path.resolve(__dirname, 'webpack.config.js');
Expand Down Expand Up @@ -50,14 +51,20 @@ export class WebpackServer implements IMochaServer {

// Wait for a build status to show up on stdout, to know when webpack is finished.
this._webpackComplete = new Promise((resolve, reject) => {
this._server.stdout!.on('data', (data) => {
// Note that data might not in general arrive at line boundaries, but in this case, it works.
const text = data.toString('utf8');
if (/compiled with.*errors/i.test(text)) { reject(new Error('Webpack failed')); }
if (/compiled successfully/i.test(text)) { resolve(true); }
let buffer = "";
this._server.stdout!.on("data", (data) => {
buffer += data.toString("utf8");
const clean = stripAnsi(buffer);
if (/compiled.*with.*errors/i.test(clean)) {
reject(new Error("Webpack failed"));
}
if (/compiled.*successfully/i.test(clean)) {
resolve(true);
}
});
});


const config = require(configPath);
const port = config.devServer.port;
this._serverUrl = `http://localhost:${port}`;
Expand Down
28 changes: 27 additions & 1 deletion test/projects/ColorSelect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ describe("ColorSelect", function() {
// click the hex value
await driver.findWait('.test-text-hex', 100).click();

await waitForSelection('.test-text-hex');

// start typing '#'
await driver.sendKeys('#');

Expand All @@ -376,6 +378,8 @@ describe("ColorSelect", function() {

// click the hex value
await driver.find('.test-text-hex').click();
await waitForSelection('.test-text-hex');


// type in #FF00FF and press enter
await driver.sendKeys(pink, Key.ENTER);
Expand All @@ -399,6 +403,9 @@ describe("ColorSelect", function() {
// click the hex value
await driver.find('.test-text-hex').click();

// Wait for focus
await waitForSelection('.test-text-hex');

// type in #0000FF
await driver.sendKeys(red);

Expand Down Expand Up @@ -432,14 +439,15 @@ describe("ColorSelect", function() {

// click the hex value
await driver.find('.test-text-hex').click();
await waitForSelection('.test-text-hex');

// start typing '#FF'
await driver.sendKeys('#FF');

// check the hex value changed
assert.equal(await driver.find('.test-text-hex').value(), '#FF');

// click button to update. We cannot send ctrl+U here, becauses it does cause picker to close
// click button to update. We cannot send ctrl+U here, because it does cause picker to close
// here.
await driver.executeScript('triggerUpdate()');

Expand Down Expand Up @@ -488,3 +496,21 @@ async function clickUnderline() {
async function clickStrikethrough() {
await driver.find('.test-font-option-FontStrikethrough').click();
}

/**
* Waits until all text in the given input element is selected and the element has focus.
* @param inputSelector Selector for the input element to check selection on.
*/
async function waitForSelection(inputSelector: string) {
await gu.waitToPass(async () => {
assert.isTrue(await driver.find(inputSelector).hasFocus());
const allSelected = await driver.executeScript(() => {
const innerSelector = arguments[0];
const el = document.querySelector(innerSelector) as HTMLInputElement;
const sel = { start: el.selectionStart, end: el.selectionEnd };
const textLen = el.value.length;
return sel.start === 0 && sel.end === textLen;
}, inputSelector);
assert.isTrue(allSelected, 'Expected all text to be selected in the text input');
});
}
2 changes: 2 additions & 0 deletions test/projects/ColumnFilterMenu2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ describe('ColumnFilterMenu2', function() {
await driver.sendKeys('A');

// click 'Apple'
await driver.findWait('.test-filter-menu-list label', 100);
await driver.findContent('.test-filter-menu-list label', /Apple/).click();

// check Apple is not included
Expand All @@ -193,6 +194,7 @@ describe('ColumnFilterMenu2', function() {
// enter 'A'
await driver.sendKeys('A');

await driver.findWait('.test-filter-menu-summary label', 100);
// check 'Other Non-Matching' is checked
assert.equal(
await driver.findContent('.test-filter-menu-summary label', /Other Non-Matching/).find('input').isSelected(),
Expand Down
18 changes: 18 additions & 0 deletions test/projects/DateRangeFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,37 +288,51 @@ describe('DateRangeFilter', function() {
await fu.findBound('max').click();
assert.equal(await fu.isOptionsVisible(), true);
await driver.sendKeys(Key.ESCAPE);
await gu.waitForMenuToClose();
assert.equal(await fu.isOptionsVisible(), false);
});

it('should show relative dates options when value changes', async function() {
await fu.findBound('min').click();
await gu.findOpenMenu();
await driver.sendKeys(Key.ESCAPE);
await gu.waitForMenuToClose();
assert.equal(await fu.isOptionsVisible(), false);
await fu.pickDateInCurrentMonth('18');
await gu.findOpenMenu();
assert.equal(await fu.isOptionsVisible(), true);
await driver.sendKeys(Key.ESCAPE);
await gu.waitForMenuToClose();
});

it('should show relative dates when selected bound changes', async function() {
await fu.findBound('min').click();
await gu.findOpenMenu();
await driver.sendKeys(Key.ESCAPE);
await gu.waitForMenuToClose();
assert.equal(await fu.isOptionsVisible(), false);
await driver.sendKeys(Key.TAB);
await gu.findOpenMenu();
assert.equal(await fu.isOptionsVisible(), true);
});

it('should toggle relative dates on click', async function() {
await fu.findBound('min').click();
await gu.findOpenMenu();
assert.equal(await fu.isOptionsVisible(), true);
await fu.findBound('min').click();
await gu.waitForMenuToClose();
assert.equal(await fu.isOptionsVisible(), false);
});

it('should show relative dates options when pressing Enter while the options are closed', async function() {
await fu.findBound('min').click();
await gu.findOpenMenu();
await driver.sendKeys(Key.ESCAPE); // Escape to close
await gu.waitForMenuToClose();
assert.equal(await fu.isOptionsVisible(), false);
await driver.sendKeys(Key.ENTER); // Enter to reopen
await gu.findOpenMenu();
assert.equal(await fu.isOptionsVisible(), true);
assert.equal(await fu.getSelected(), 'min');
});
Expand All @@ -331,6 +345,7 @@ describe('DateRangeFilter', function() {

it('should have working keyboard navigation after picking date from calendar', async function() {
await fu.findBound('min').click();
await gu.findOpenMenu();
assert.deepEqual(await fu.getSelectedOption(), []);
await fu.pickDateInCurrentMonth('18');
assert.deepEqual(await fu.getSelectedOption(), ['2022-09-18']);
Expand Down Expand Up @@ -392,6 +407,8 @@ describe('DateRangeFilter', function() {
// click Today
await driver.findContent('.test-filter-menu-presets-links button', 'Today').click();

await gu.findOpenMenu();

// check min bounds shows 'today'
assert.equal(await fu.getBoundText('min'), 'Today');

Expand All @@ -400,6 +417,7 @@ describe('DateRangeFilter', function() {

// click Last week
await driver.findContent('.test-filter-menu-presets-links button', 'More').click();
await gu.findOpenMenu();
await driver.findContent('.grist-floating-menu li', 'Last Week').click();

// check min bounds shows '1st day of last week'
Expand Down
6 changes: 4 additions & 2 deletions test/projects/PagesComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('PagesComponent', function() {
});

function findPage(name: RegExp) {
return driver.findContent('.test-treeview-label', name);
return driver.findContentWait('.test-treeview-label', name, 100);
}

async function beginRenaming(name: RegExp) {
Expand All @@ -28,7 +28,9 @@ describe('PagesComponent', function() {
await beginRenaming(/Interactions/);

// click on the right part of the text input
await driver.find('.test-docpage-editor').mouseMove({x: 100}).click();
await driver.find('.test-docpage-editor').click();

await driver.sendKeys(Key.END); // move to the end

// enter 'Renamed'
await driver.sendKeys('Renamed', Key.ENTER);
Expand Down
26 changes: 16 additions & 10 deletions test/projects/UserManager.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {assert, driver, Key, WebElement} from 'mocha-webdriver';
import {server, setupTestSuite} from 'test/projects/testUtils';
import * as gu from 'test/nbrowser/gristUtils';


describe('UserManager', () => {
setupTestSuite();
gu.bigScreen();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? (I always have to adjust when I run such tests locally because bigScreen is bigger than my physical screen)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is. It will fail with the full browser without it. But the size of the window can be adjusted globally (Cyprian already raised that issue before), I will propose a change as a DIFF as more test will be affected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have gu.bigScreen('medium'), and to me that would be a more convenient default. Totally fine to leave as is in this PR.


before(async function() {
this.timeout(60000); // Set a longer default timeout.
Expand Down Expand Up @@ -34,12 +37,14 @@ describe('UserManager', () => {
}

it('should render all emails and roles initially', async function() {
assert.deepEqual(await getRenderedMembers(), [
["[email protected]", "Owner"],
["[email protected]", "Editor"],
["[email protected]", "Viewer"],
["[email protected]", "Viewer"],
]);
await gu.waitToPass(async () => {
assert.deepEqual(await getRenderedMembers(), [
["[email protected]", "Owner"],
["[email protected]", "Editor"],
["[email protected]", "Viewer"],
["[email protected]", "Viewer"],
]);
}, 5000);
assert.deepEqual(JSON.parse(await driver.find('.test-result').getText()), {});
});

Expand All @@ -65,10 +70,11 @@ describe('UserManager', () => {
await driver.find('.test-um-member-new input').sendKeys('[email protected]', Key.ENTER);
await driver.find('.test-um-member-new input').sendKeys('[email protected]', Key.ENTER);
await driver.find('.test-um-member-new input').sendKeys('[email protected]', Key.ENTER);

await driver.findContent('.test-um-member', /eve@a\.com/).find('.test-um-member-role').doClick();
await driver.findContent('.test-um-role-option', /Editor/).doClick();
await driver.findContentWait('.test-um-role-option', /Editor/, 100).doClick();
await driver.findContent('.test-um-member', /bob@bob\.tail/).find('.test-um-member-role').doClick();
await driver.findContent('.test-um-role-option', /Editor/).doClick();
await driver.findContentWait('.test-um-role-option', /Editor/, 100).doClick();

assert.deepEqual(await getRenderedMembers(), [
["[email protected]", "Owner"],
Expand Down Expand Up @@ -130,9 +136,9 @@ describe('UserManager', () => {
await driver.find('.test-um-member-new input').sendKeys('[email protected]', Key.ENTER);
await driver.findContent('.test-um-member', /foo@example\.com/).find('.test-um-member-delete').doClick();
await driver.findContent('.test-um-member', /bar@example\.com/).find('.test-um-member-role').doClick();
await driver.findContent('.test-um-role-option', /Owner/).doClick();
await driver.findContentWait('.test-um-role-option', /Owner/, 100).doClick();
await driver.findContent('.test-um-member', /alice@bobtail\.com/).find('.test-um-member-role').doClick();
await driver.findContent('.test-um-role-option', /Owner/).doClick();
await driver.findContentWait('.test-um-role-option', /Owner/, 100).doClick();

assert.deepEqual(await getRenderedMembers(), [
["[email protected]", null],
Expand Down
4 changes: 2 additions & 2 deletions test/projects/contextMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('contextMenu', function() {
await contextMenu();

// check action worked
await driver.findContent('.grist-floating-menu li', 'Reset').click();
await driver.findContentWait('.grist-floating-menu li', 'Reset', 100).click();
assert.deepEqual(
await driver.findAll('.test-logs', e => e.getText()),
[]
Expand All @@ -74,7 +74,7 @@ describe('contextMenu', function() {
await driver.sendKeys(Key.ESCAPE);

// check menu is closed
assert.equal(await driver.find('.grist-floating-menu').isPresent(), false);
await gu.waitForMenuToClose();
});

it('should support arrow navigation', async function() {
Expand Down
6 changes: 4 additions & 2 deletions test/projects/modals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ describe('modals', function() {
});

async function checkClosed() {
await driver.sleep(50); // Give time for the closing animation to finish.
assert.equal(await driver.find('.test-modal-dialog').isPresent(), false);
}

async function checkOpen() {
assert.equal(await driver.find('.test-modal-dialog').isDisplayed(), true);
assert.equal(await driver.findWait('.test-modal-dialog', 100).isPresent(), true);
}

it('should close on click-away, OK, Cancel, Escape, Enter', async function() {
// Modal is initially reported as "Cancelled" and isn't present.
assert.match(await driver.find('.testui-confirm-modal-text').getText(), /Cancelled/);
assert.match(await driver.findWait('.testui-confirm-modal-text', 100).getText(), /Cancelled/);
await checkClosed();

// Click on Cancel closes.
Expand Down
Loading