Skip to content

Commit

Permalink
Integration tests admin UI (keystonejs#6260)
Browse files Browse the repository at this point in the history
* navigation tests

* more navigation related tests

* commit out bad test

* update schema.graphql

* update nav tests

* update schema.graphql

* update tests.yml to include navigation admin-ui test

* fix tests

* add determinism to init.test.ts

* update nav test to be a bit more deterministic

* update init.test.ts

* rename seedData fn and move it to utils module

* remove log

* update util for better errors

* remove unnecessary try catch
  • Loading branch information
gwyneplaine authored and Nikitoring committed Sep 14, 2021
1 parent a90f62e commit 9c0681c
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ jobs:
DATABASE_URL: 'file:./test.db'
strategy:
matrix:
test: ['init.test.ts', 'list-view-crud.test.ts']
test: ['init.test.ts', 'list-view-crud.test.ts', 'navigation.test.ts']
fail-fast: false
steps:
- name: Checkout Repo
Expand Down
10 changes: 10 additions & 0 deletions tests/admin-ui-tests/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ adminUITests('./tests/test-projects/basic', browserType => {
test('Task List card should be visible', async () => {
await page.waitForSelector('h3:has-text("Task")');
});
test('Clicking on the logo should return you to the Dashboard route', async () => {
await page.goto('http://localhost:3000/tasks');
await page.waitForSelector('h3 a:has-text("Keystone 6")');
await Promise.all([
page.waitForNavigation({
url: 'http://localhost:3000',
}),
page.click('h3 a:has-text("Keystone 6")'),
]);
});
test('Should see a 404 on request of the /init route', async () => {
await page.goto('http://localhost:3000/init');
const content = await page.textContent('body h1');
Expand Down
30 changes: 5 additions & 25 deletions tests/admin-ui-tests/list-view-crud.test.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,10 @@
import { Browser, Page } from 'playwright';
import fetch from 'node-fetch';
import { adminUITests, deleteAllData } from './utils';

import { makeGqlRequest, adminUITests, deleteAllData } from './utils';

adminUITests('./tests/test-projects/crud-notifications', browserType => {
let browser: Browser = undefined as any;
let page: Page = undefined as any;
const seedData = async (query: string, variables?: Record<string, any>) => {
try {
const { errors } = await fetch('http://localhost:3000/api/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
variables,
}),
}).then(res => res.json());
if (errors) {
throw errors;
}
} catch (e) {
console.log(e);
throw e;
}
};

beforeAll(async () => {
browser = await browserType.launch();
Expand All @@ -46,7 +26,7 @@ adminUITests('./tests/test-projects/crud-notifications', browserType => {
}
}
`;
await seedData(query);
await makeGqlRequest(query);
await Promise.all([page.waitForNavigation(), page.goto('http://localhost:3000/tasks')]);
await page.waitForSelector('tbody tr:first-of-type td:first-of-type label');
await page.click('tbody tr:first-of-type td:first-of-type label');
Expand All @@ -71,7 +51,7 @@ adminUITests('./tests/test-projects/crud-notifications', browserType => {
}
}
`;
await seedData(query);
await makeGqlRequest(query);
await Promise.all([page.waitForNavigation(), page.goto('http://localhost:3000/tasks')]);
await page.click('tbody tr:first-of-type td:first-of-type label');
await page.click('button:has-text("Delete")');
Expand Down Expand Up @@ -105,7 +85,7 @@ adminUITests('./tests/test-projects/crud-notifications', browserType => {
}
}),
};
await seedData(query, variables);
await makeGqlRequest(query, variables);
await Promise.all([
page.waitForNavigation(),
page.goto('http://localhost:3000/tasks?sortBy=label&page=1'),
Expand Down
75 changes: 75 additions & 0 deletions tests/admin-ui-tests/navigation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Browser, Page } from 'playwright';
import { adminUITests } from './utils';

adminUITests('./tests/test-projects/basic', browserType => {
let browser: Browser = undefined as any;
let page: Page = undefined as any;

beforeAll(async () => {
browser = await browserType.launch();
page = await browser.newPage();
await page.goto('http://localhost:3000');
});
test('Nav contains a Dashboard route by default', async () => {
await page.waitForSelector('nav a:has-text("Dashboard")');
});
test('When at the index, the Dashboard NavItem is selected', async () => {
const element = await page.waitForSelector('nav a:has-text("Dashboard")');
const ariaCurrent = await element?.getAttribute('aria-current');
expect(ariaCurrent).toBe('location');
});
test('When navigated to a List route, the representative list NavItem is selected', async () => {
await page.goto('http://localhost:3000/tasks');
const element = await page.waitForSelector('nav a:has-text("Tasks")');
const ariaCurrent = await element?.getAttribute('aria-current');
expect(ariaCurrent).toBe('location');
});
test('Can access all list pages via the navigation', async () => {
await page.goto('http://localhost:3000');
await Promise.all([
page.waitForNavigation({
url: 'http://localhost:3000/tasks',
}),
page.click('nav a:has-text("Tasks")'),
]);
await Promise.all([
page.waitForNavigation({
url: 'http://localhost:3000/people',
}),
page.click('nav a:has-text("People")'),
]);
});
test('Can not access hidden lists via the navigation', async () => {
await Promise.all([page.waitForNavigation(), page.goto('http://localhost:3000')]);
await page.waitForSelector('nav');
const navItems = await page.$$('nav li a');
const navLinks = await Promise.all(
navItems.map(navItem => {
return navItem.getAttribute('href');
})
);
expect(navLinks.length).toBe(3);
expect(navLinks.includes('/secretplans')).toBe(false);
});
test('When navigated to an Item view, the representative list NavItem is selected', async () => {
await page.goto('http://localhost:3000');
await page.click('button[title="Create Task"]');
await page.fill('id=label', 'Test Task');
await Promise.all([page.waitForNavigation(), page.click('button[type="submit"]')]);
const element = await page.waitForSelector('nav a:has-text("Tasks")');
const ariaCurrent = await element?.getAttribute('aria-current');
expect(ariaCurrent).toBe('location');
});
test('When pressing a list view nav item from an item view, the correct route should be reached', async () => {
await page.goto('http://localhost:3000');
await page.click('button[title="Create Task"]');
await page.fill('id=label', 'Test Task');
await Promise.all([page.waitForNavigation(), page.click('button[type="submit"]')]);
await Promise.all([page.waitForNavigation(), page.click('nav a:has-text("Tasks")')]);

expect(page.url()).toBe('http://localhost:3000/tasks');
});
afterAll(async () => {
await browser.close();
});
});
18 changes: 18 additions & 0 deletions tests/admin-ui-tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'path';
import fs from 'fs';
import { promisify } from 'util';
import fetch from 'node-fetch';
import execa from 'execa';
import _treeKill from 'tree-kill';
import * as playwright from 'playwright';
Expand All @@ -21,6 +22,23 @@ const promiseSignal = (): Promise<void> & { resolve: () => void } => {
};
const projectRoot = findRootSync(process.cwd());

export const makeGqlRequest = async (query: string, variables?: Record<string, any>) => {
const { errors } = await fetch('http://localhost:3000/api/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
variables,
}),
}).then(res => res.json());

if (errors) {
throw new Error(`graphql errors: ${errors.map((x: Error) => x.message).join('\n')}`);
}
};

export const deleteAllData: (projectDir: string) => Promise<void> = async (projectDir: string) => {
const { PrismaClient } = require(path.resolve(
projectRoot,
Expand Down
53 changes: 53 additions & 0 deletions tests/test-projects/basic/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,42 @@ input TaskRelateToManyForCreateInput {
connect: [TaskWhereUniqueInput!]
}

type SecretPlan {
id: ID!
label: String
description: String
}

input SecretPlanWhereUniqueInput {
id: ID
}

input SecretPlanWhereInput {
AND: [SecretPlanWhereInput!]
OR: [SecretPlanWhereInput!]
NOT: [SecretPlanWhereInput!]
id: IDFilter
}

input SecretPlanOrderByInput {
id: OrderDirection
}

input SecretPlanUpdateInput {
label: String
description: String
}

input SecretPlanUpdateArgs {
where: SecretPlanWhereUniqueInput!
data: SecretPlanUpdateInput!
}

input SecretPlanCreateInput {
label: String
description: String
}

"""
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
Expand All @@ -223,6 +259,15 @@ type Mutation {
updatePeople(data: [PersonUpdateArgs!]!): [Person]
deletePerson(where: PersonWhereUniqueInput!): Person
deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
createSecretPlan(data: SecretPlanCreateInput!): SecretPlan
createSecretPlans(data: [SecretPlanCreateInput!]!): [SecretPlan]
updateSecretPlan(
where: SecretPlanWhereUniqueInput!
data: SecretPlanUpdateInput!
): SecretPlan
updateSecretPlans(data: [SecretPlanUpdateArgs!]!): [SecretPlan]
deleteSecretPlan(where: SecretPlanWhereUniqueInput!): SecretPlan
deleteSecretPlans(where: [SecretPlanWhereUniqueInput!]!): [SecretPlan]
}

type Query {
Expand All @@ -242,6 +287,14 @@ type Query {
): [Person!]
person(where: PersonWhereUniqueInput!): Person
peopleCount(where: PersonWhereInput! = {}): Int
secretPlans(
where: SecretPlanWhereInput! = {}
orderBy: [SecretPlanOrderByInput!]! = []
take: Int
skip: Int! = 0
): [SecretPlan!]
secretPlan(where: SecretPlanWhereUniqueInput!): SecretPlan
secretPlansCount(where: SecretPlanWhereInput! = {}): Int
keystone: KeystoneMeta!
}

Expand Down
6 changes: 6 additions & 0 deletions tests/test-projects/basic/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ model Person {
id String @id @default(cuid())
name String?
tasks Task[] @relation("Task_assignedTo")
}

model SecretPlan {
id String @id @default(cuid())
label String?
description String?
}
9 changes: 9 additions & 0 deletions tests/test-projects/basic/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,13 @@ export const lists = createSchema({
defaultIsFilterable: true,
defaultIsOrderable: true,
}),
SecretPlan: list({
fields: {
label: text(),
description: text(),
},
ui: {
isHidden: true,
},
}),
});

0 comments on commit 9c0681c

Please sign in to comment.