Skip to content
This repository has been archived by the owner on Feb 23, 2022. It is now read-only.

Commit

Permalink
Merge pull request #311 from multinet-app/simplify-client-testing
Browse files Browse the repository at this point in the history
Simplify client testing
  • Loading branch information
waxlamp committed Feb 11, 2020
2 parents 2ab60ad + a32a079 commit 6d003be
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 72 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ jobs:
- run: cd client && yarn install
- run: cd client && yarn lint
- run: cd client && yarn lint:test
- run: pipenv run test-server-up
- run: cd client && yarn test:client:up
- run: cd client && yarn test
- run: cd client && yarn build

Expand Down
3 changes: 1 addition & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
"test:client:up": "sh ./scripts/start.sh",
"test:client:down": "sh ./scripts/stop.sh",
"test:client:restart": "yarn test:client:down; yarn test:client:up",
"test:cmd": "tape -r esm test/**/*.test.js | tap-spec",
"test": "yarn test:server:restart && yarn test:client:restart && yarn test:cmd; code=$?; yarn test:server:down; yarn test:client:down; if [ ${code} = 0 ]; then true; else false; fi"
"test": "tape -r esm test/**/*.test.js | tap-spec"
},
"dependencies": {
"axios": "^0.18.1",
Expand Down
6 changes: 3 additions & 3 deletions client/scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ if [ -e server.pid ]; then
exit 1
fi

FLASK_SERVE_PORT=50000 nohup yarn serve --port 58080 >server.out &
FLASK_SERVE_PORT=5000 nohup yarn serve --port 8080 >server.out &
echo $! >server.pid

# Loop until the client is up.
Expand All @@ -15,7 +15,7 @@ count=0

echo -n "waiting for client to come up"
while [ ${started} = 0 ] && [ ${count} -lt 30 ]; do
headers=$(curl -s -I --max-time 0.5 http://localhost:58080/api/workspaces)
headers=$(curl -s -I --max-time 0.5 http://localhost:8080/api/workspaces)
curl_status=$?

if [ ${curl_status} = 0 ]; then
Expand All @@ -35,4 +35,4 @@ if [ ${started} = 1 ]; then
true
else
false
fi
fi
2 changes: 2 additions & 0 deletions client/src/components/DeleteWorkspaceDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<template v-slot:activator="{ on: tooltip }">
<v-scroll-x-transition>
<v-btn
id="delete-workspaces"
icon
small
text
Expand Down Expand Up @@ -42,6 +43,7 @@
<v-card-actions class="px-4 py-3">
<v-spacer />
<v-btn
id="delete-workspace-yes"
depressed
color="error"
@click="execute"
Expand Down
1 change: 1 addition & 0 deletions client/src/components/WorkspaceDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<v-card-actions>
<v-spacer />
<v-btn
id="got-it"
color="primary"
@click="popover = false"
small
Expand Down
191 changes: 130 additions & 61 deletions client/test/e2e.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,54 @@ process.on('unhandledRejection', (error) => {
const width = 1920;
const height = 1080;

const host = process.env.HOST || 'localhost';
const port = process.env.PORT || '8080';
const url = `http://${host}:${port}/`;

const debug = !!process.env.DEBUG;

// Opens the chromium window
function browser(width, height) {
return puppeteer.launch({
headless: true,
headless: !debug,
args: [`--window-size=${width},${height}`],
// slowMo: 20 // For testing
slowMo: debug ? 250 : 0,
});
}

// Sets up the browser with some default settings
async function setup() {
// Create a browser.
const b = await browser(width, height);

// Create a page.
const p = await b.newPage();
await p.setViewport({ width, height });
await p.goto('http://127.0.0.1:58080/');
await p.setDefaultTimeout(5000);

// Navigate to the app, and dismiss the "got it" dialog.
await p.goto(url);
await p.waitForSelector('#got-it');
await sleep(2000);
await p.click('#got-it');

return [b, p];
}

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

async function get_element_coords(p, selector) {
const el = await p.$(selector);
const bb = await el.boundingBox();

return {
x: bb.x + bb.width / 2,
y: bb.y + bb.height / 2,
};
}

// Clicks the add workspace button, types a name, and clicks the button
async function create_workspace(p, name) {
await p.waitForSelector('#add-workspace');
Expand All @@ -46,22 +71,42 @@ async function create_workspace(p, name) {
await p.click('#create-workspace');
}

// Checks that a workspace exists in the left pane
async function workspace_exists(p, name) {
let exists = false;
async function delete_workspace(p, name) {
// Move the mouse over the workspace entry to make the checkbox appear.
const entry = `a[href="#/workspaces/${name}/"]`;
const coords = await get_element_coords(p, entry);
await p.mouse.move(coords.x, coords.y);

// Click on the checkbox, then on the delete icon.
const selector = `${entry} input`;
await p.waitForSelector(selector);
await p.click(selector);
await p.click('#delete-workspaces');

// Wait for the delete dialog to appear, and for the "Yes" button to become
// active.
await sleep(4000);

// Click on the "yes".
await p.click('#delete-workspace-yes');
}

await p.waitForSelector('.v-list-item__title');
let workspaces = await p.evaluate(() => {
async function get_workspace_names(p) {
return p.evaluate(() => {
let titles = [];
let doc_nodes = document.querySelectorAll('.v-list-item__title');
for (let node of doc_nodes) {
titles.push(node.innerText);
}
return titles;
});
}

// Checks that a workspace exists in the left pane
async function workspace_exists(p, name) {
const workspaces = await get_workspace_names(p);

exists = workspaces.includes(name);
return exists;
return workspaces.includes(name);
}

// Get the names of either all tables or all graphs in the current workspace
Expand All @@ -82,16 +127,6 @@ async function get_element_names(element_type, p) {
return tables;
}

// Checks if a table or graph exists in the current workspace
async function element_exists(element_type, p, name) {
let exists, tables;

tables = await get_element_names(element_type, p);

exists = tables.includes(name);
return exists;
}

// Checks if no tables or graphs exist in the current workspace
async function elements_empty(element_type, p) {
let list_is_empty, tables;
Expand All @@ -102,62 +137,96 @@ async function elements_empty(element_type, p) {
return list_is_empty;
}

// Declare global variables for the browser and page objects.
let b;
let p;

test('Create a valid workspace', async (t) => {
// Set up the browser/page.
[b, p] = await setup();

// First, figure out a name we can use for the workspace.
const workspaces = await get_workspace_names(p);
let name;
const limit = 1000;
let i;
for (i = 0; i < limit; i++) {
name = `puppeteer${i}`;
if (!workspaces.includes(name)) {
break;
}
}
if (i === limit) {
throw new Error('fatal: could not find an unused name');
}

// Start of tests
test('e2e - Check that actions that should work, do work', async (t) => {
// Arrange: Set up the page
const [b, p] = await setup();

// Act: Test creating a workspace
await create_workspace(p, 'puppeteer');
// Create the workspace.
await create_workspace(p, name);
await sleep(500);

// Assert: Check that the new workspace exists with no tables
let exists = await workspace_exists(p, 'puppeteer');
t.ok(exists, 'Workspace called "puppeteer" was created.');
// Check that the new workspace now exists.
const exists = await workspace_exists(p, name);
t.ok(exists, `Workspace "${name}" was created`);

// Assert: Check that there are no tables or graphs yet
exists = await elements_empty('Tables', p, undefined);
// Check that the new workspace has no tables or networks.
const tables = await elements_empty('Tables', p);
t.ok(tables, 'The new workspace has no tables');

t.ok(exists, 'The new workspace has no tables.');
const networks = await elements_empty('Networks', p);
t.ok(networks, 'The new workspace has no networks');

exists = await elements_empty('Networks', p, undefined);
t.ok(exists, 'The new workspace has no networks.');
// Delete the workspace.
await delete_workspace(p, name);
await sleep(1000);
const deleted = !await workspace_exists(p, name);
t.ok(deleted, `Workspace "${name}" was deleted`);

// Cleanup
await b.close();
t.end();
});

test('e2e - Check that actions that shouldn\'t work, don\'t work', async (t) => {
// Arrange: Set up the page
const [b, p] = await setup();

// Act: Test creating invalid workspaces
await create_workspace(p, '123');
await p.click('#workspace-name', { clickCount: 3 });
await p.click('#add-workspace'); // Close the modal (this will cause a failure in the next command if it is made)

await create_workspace(p, '++--==__');
await p.click('#workspace-name', { clickCount: 3 });
await p.click('#add-workspace'); // Close the modal (this will cause a failure in the next command if it is made)
test('Create a workspace with an invalid name (consisting of numbers)', async (t) => {
const workspaces = await get_workspace_names(p);
let name;
const limit = 1000;
for (name = 123; name < limit; name++) {
if (!workspaces.includes(`${name}`)) {
break;
}
}
if (name === limit) {
throw new Error('fatal: could not find an unused name');
}
name = `${name}`;

await create_workspace(p, name);
await p.click('#workspace-name', {
clickCount: 3,
});
await p.click('#add-workspace');

await create_workspace(p, 'a');
const workspaces2 = await get_workspace_names(p);
t.ok(!workspaces2.includes(name), `Workspace with invalid name "${name}" was not created`);

// Assert: Check that the new workspace exists with no tables
await sleep(200);
let exists = await workspace_exists(p, '123');
t.notOk(exists, 'Workspace called "123" wasn\'t created.');
t.end();
});

exists = await workspace_exists(p, '++--==__');
t.notOk(exists, 'Workspace called "++--==__" wasn\'t created.');
test('Create a workspace with an invalid name (consisting of punctuation)', async (t) => {
const workspaces = await get_workspace_names(p);
const name = '++--==__';
if (workspaces.includes(name)) {
throw new Error('fatal: could not find an unused name');
}

exists = await workspace_exists(p, 'a');
t.ok(exists, 'Workspace called "a" was created.');
await create_workspace(p, name);
await p.click('#workspace-name', {
clickCount: 3,
});
await p.click('#add-workspace');

exists = await element_exists('Tables', p, 'broken');
t.notOk(exists, 'New workspaces don\'t have tables');
const workspaces2 = await get_workspace_names(p);
t.ok(!workspaces2.includes(name), `Workspace with invalid name "${name}" was not created`);

// Cleanup
await b.close();

t.end();
});
3 changes: 1 addition & 2 deletions multinetjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@
"test:server:down": "cd .. && PIPENV_DONT_LOAD_ENV=1 pipenv run test-server-down",
"test:server:clean": "cd .. && PIPENV_DONT_LOAD_ENV=1 pipenv run test-server-clean",
"test:server:restart": "yarn test:server:down; yarn test:server:clean && yarn test:server:up",
"test:cmd": "tape -r esm test/**/*.test.js | tap-spec",
"test": "yarn test:server:restart && yarn test:cmd; code=$?; yarn test:server:down; if [ ${code} = 0 ]; then true; else false; fi"
"test": "tape -r esm test/**/*.test.js | tap-spec"
},
"author": "Kitware, Inc.",
"license": "Apache-2.0",
Expand Down
2 changes: 1 addition & 1 deletion multinetjs/test/multinet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test('multinet test', async (t) => {
t.ok(multinetApi, 'multinetApi() was imported successfully');
t.equal(typeof multinetApi, 'function', 'multinetApi() is a function');

const api = multinetApi('http://localhost:50000/api');
const api = multinetApi('http://localhost:5000/api');
t.ok(api, 'multinetApi() gave us an API object');

try {
Expand Down
6 changes: 3 additions & 3 deletions scripts/server/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ if [ -e server.pid ]; then
fi

# Launch the server in the background.
PIPENV_DONT_LOAD_ENV=1 FLASK_APP=multinet FLASK_ENV=development FLASK_SERVE_PORT=50000 ARANGO_PORT=58529 nohup pipenv run serve >server.out &
PIPENV_DONT_LOAD_ENV=1 FLASK_APP=multinet FLASK_ENV=development FLASK_SERVE_PORT=5000 ARANGO_PORT=8529 nohup pipenv run serve >server.out &
echo $! >server.pid

# Start the Arango database in the background with a clean data directory.
ARANGO_PORT=58529 ARANGO_DATA=$(readlink -f arango) docker-compose -p testing up -d
ARANGO_PORT=8529 ARANGO_DATA=$(readlink -f arango) docker-compose -p testing up -d

# Loop until the server is up.
started=0
count=0

echo -n "waiting for server to come up"
while [ ${started} = 0 ] && [ ${count} -lt 30 ]; do
headers=$(curl -s -I http://localhost:50000/api/workspaces)
headers=$(curl -s -I http://localhost:5000/api/workspaces)
curl_status=$?

if [ ${curl_status} = 0 ]; then
Expand Down

0 comments on commit 6d003be

Please sign in to comment.