Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add extension e2e tests #1587

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
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
67 changes: 67 additions & 0 deletions e2e-tests/fixtures/Extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { APIRequestContext, Page } from '@playwright/test';
import { getUserCookieValue } from '../utilities/helpers';

export class Extension {
async createExtension(page: Page, request: APIRequestContext, extensionName: string): Promise<number | undefined> {
const cookie = getUserCookieValue(await page.context().cookies());

if (cookie !== undefined) {
const response = await request.post('http://localhost:8080/v1/graphql/', {
data: JSON.stringify({
query: `
mutation InsertExtension {
insert_extensions(objects: {
label: "${extensionName}",
description: "Description for ${extensionName}",
url: "http://localhost:8080",
extension_roles: {
data: {
role: aerie_admin
}
}
}) {
returning {
id
}
}
}`,
}),
headers: {
Authorization: `Bearer ${cookie}`,
'Content-Type': 'application/json',
},
});

const data = (await response.json()).data.insert_extensions.returning;

return data[0].id as number;
}
}

async deleteExtension(page: Page, request: APIRequestContext, id: number): Promise<void> {
const cookie = getUserCookieValue(await page.context().cookies());

if (cookie !== undefined) {
await request.post('http://localhost:8080/v1/graphql/', {
data: JSON.stringify({
query: `
mutation DeleteExtension {
delete_extensions(where: {
id: {
_eq: ${id}
}
}) {
returning {
id
}
}
}`,
}),
headers: {
Authorization: `Bearer ${cookie}`,
'Content-Type': 'application/json',
},
});
}
}
}
4 changes: 4 additions & 0 deletions e2e-tests/fixtures/Plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export class Plan {
navButtonConstraintsMenu: Locator;
navButtonExpansion: Locator;
navButtonExpansionMenu: Locator;
navButtonExtension: Locator;
navButtonExtensionMenu: Locator;
navButtonScheduling: Locator;
navButtonSchedulingMenu: Locator;
navButtonSimulation: Locator;
Expand Down Expand Up @@ -513,6 +515,8 @@ export class Plan {
this.navButtonActivityCheckingMenu = this.navButtonActivityChecking.getByRole('menu');
this.navButtonExpansion = page.locator(`.nav-button:has-text("Expansion")`);
this.navButtonExpansionMenu = this.navButtonExpansion.getByRole('menu');
this.navButtonExtension = page.locator(`.nav-button:has-text("Extensions")`);
this.navButtonExtensionMenu = this.navButtonExtension.getByRole('menu');
this.navButtonConstraints = page.locator(`.nav-button:has-text("Constraints")`);
this.navButtonConstraintsMenu = this.navButtonConstraints.getByRole('menu');
this.navButtonScheduling = page.locator(`.nav-button:has-text("Scheduling")`);
Expand Down
84 changes: 84 additions & 0 deletions e2e-tests/tests/extension.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import test, { BrowserContext, expect, Page } from '@playwright/test';
import { adjectives, animals, colors, uniqueNamesGenerator } from 'unique-names-generator';
import { Constraints } from '../fixtures/Constraints';
import { Extension } from '../fixtures/Extension';
import { Models } from '../fixtures/Models';
import { Plan } from '../fixtures/Plan';
import { Plans } from '../fixtures/Plans';
import { SchedulingConditions } from '../fixtures/SchedulingConditions';
import { SchedulingGoals } from '../fixtures/SchedulingGoals';

let extension: Extension;
let extensionName: string;
let extensionId: number | undefined;
let constraints: Constraints;
let context: BrowserContext;
let models: Models;
let page: Page;
let plan: Plan;
let plans: Plans;
let schedulingConditions: SchedulingConditions;
let schedulingGoals: SchedulingGoals;

test.beforeAll(async ({ baseURL, browser, request }) => {
context = await browser.newContext();
page = await context.newPage();

extension = new Extension();
extensionName = uniqueNamesGenerator({ dictionaries: [adjectives, colors, animals] });
extensionId = await extension.createExtension(page, request, extensionName);

models = new Models(page);
plans = new Plans(page, models);
constraints = new Constraints(page);
schedulingConditions = new SchedulingConditions(page);
schedulingGoals = new SchedulingGoals(page);
plan = new Plan(page, plans, constraints, schedulingGoals, schedulingConditions);

await models.goto();
await models.createModel(baseURL);
await plans.goto();
await plans.createPlan();
await plan.goto();
});

test.afterAll(async () => {
await plan.deleteAllActivities();
await plans.goto();
await plans.deletePlan();
await models.goto();
await models.deleteModel();
await page.close();
await context.close();
});

test.describe.serial('Extensions', () => {
test(`Hovering on 'Extensions' in the top navigation bar should show the extension menu`, async () => {
await expect(plan.navButtonExtensionMenu).not.toBeVisible();
plan.navButtonExtension.hover();
await expect(plan.navButtonExtensionMenu).toBeVisible();
plan.planTitle.hover();
await expect(plan.navButtonExtensionMenu).not.toBeVisible();
});

test(`The extension that we created before the tests should be in the extension menu`, async () => {
plan.navButtonExtension.hover();
await expect(plan.navButtonExtensionMenu).toBeVisible();
await expect(plan.navButtonExtensionMenu.getByRole('menuitem', { name: extensionName })).toBeVisible();
});

test(`Clicking the extension should invoke the http call`, async () => {
plan.navButtonExtension.hover();
const extensionRequest = page.waitForRequest('http://localhost:3000/extensions');
plan.navButtonExtensionMenu.getByRole('menuitem', { name: extensionName }).click();
expect((await (await extensionRequest).response())?.ok).toBeTruthy();
});

test(`Delete an extension`, async ({ page, request }) => {
if (extensionId !== undefined) {
await extension.deleteExtension(page, request, extensionId);
await expect(plan.navButtonExtensionMenu).not.toBeVisible();
await expect(plan.navButtonExtension).not.toBeVisible();
}
});
});
5 changes: 5 additions & 0 deletions e2e-tests/tests/plan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ test.describe.serial('Plan', () => {
await expect(plan.navButtonSchedulingMenu).not.toBeVisible();
});

test(`By default the extension menu should not show because there are no extensions`, async () => {
await expect(plan.navButtonExtension).not.toBeVisible();
await expect(plan.navButtonExtensionMenu).not.toBeVisible();
});

test(`Changing to a new plan should clear the selected activity`, async ({ baseURL }) => {
await plan.showPanel(PanelNames.TIMELINE_ITEMS);

Expand Down
11 changes: 11 additions & 0 deletions e2e-tests/utilities/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Cookie } from '@playwright/test';

export function getUserCookieValue(cookies: Cookie[]): string | undefined {
for (const cookie of cookies) {
if (cookie.name === 'user') {
return JSON.parse(atob(cookie.value)).token;
}
}

return undefined;
}
3 changes: 2 additions & 1 deletion src/routes/plans/[id]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
simulationDatasetErrors,
} from '../../../stores/errors';
import { planExpansionStatus, resetExpansionStores, selectedExpansionSetId } from '../../../stores/expansion';
import { extensions } from '../../../stores/extensions';
import {
activityTypes,
initialPlan,
Expand Down Expand Up @@ -791,7 +792,7 @@
</svelte:fragment>
</PlanNavButton>
<ExtensionMenu
extensions={data.extensions}
extensions={$extensions}
title={!compactNavMode ? 'Extensions' : ''}
user={data.user}
on:callExtension={onCallExtension}
Expand Down
2 changes: 0 additions & 2 deletions src/routes/plans/[id]/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ export const load: PageLoad = async ({ parent, params, url }) => {
initialPlan.model.view,
);
const initialPlanSnapshotId = getSearchParameterNumber(SearchParameters.SNAPSHOT_ID, url.searchParams);
const extensions = await effects.getExtensions(user);

return {
extensions,
initialActivityTypes,
initialPlan,
initialPlanSnapshotId,
Expand Down
5 changes: 5 additions & 0 deletions src/stores/extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { Extension } from '../types/extension';
import gql from '../utilities/gql';
import { gqlSubscribable } from './subscribable';

export const extensions = gqlSubscribable<Extension[]>(gql.SUB_EXTENSIONS, {}, [], null);
15 changes: 0 additions & 15 deletions src/utilities/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3627,21 +3627,6 @@ const effects = {
}
},

async getExtensions(user: User | null): Promise<Extension[]> {
try {
const data = await reqHasura<Extension[]>(gql.GET_EXTENSIONS, {}, user);
const { extensions = [] } = data;
if (extensions != null) {
return extensions;
} else {
throw Error('Unable to retrieve extensions');
}
} catch (e) {
catchError(e as Error);
return [];
}
},

async getExternalEventTypes(plan_id: number, user: User | null): Promise<ExternalEventType[]> {
try {
const sourceData = await reqHasura<
Expand Down
32 changes: 16 additions & 16 deletions src/utilities/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1460,22 +1460,6 @@ const gql = {
}
`,

GET_EXTENSIONS: `#graphql
query GetExtensions {
${Queries.EXTENSIONS} {
description
extension_roles {
extension_id
role
}
id
label
updated_at
url
}
}
`,

GET_EXTERNAL_EVENTS: `#graphql
query GetExternalEvents(
$sourceKey: String!,
Expand Down Expand Up @@ -2467,6 +2451,22 @@ const gql = {
}
`,

SUB_EXTENSIONS: `#graphql
subscription SubExtensions {
${Queries.EXTENSIONS} {
description
extension_roles {
extension_id
role
}
id
label
updated_at
url
}
}
`,

SUB_EXTERNAL_EVENT_TYPES: `#graphql
subscription SubExternalEventTypes {
models: ${Queries.EXTERNAL_EVENT_TYPES}(order_by: { name: asc }) {
Expand Down
2 changes: 1 addition & 1 deletion src/utilities/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,6 @@ const queryPermissions: Record<GQLKeys, (user: User | null, ...args: any[]) => b
return isUserAdmin(user) || getPermission([Queries.SEQUENCE_TO_SIMULATED_ACTIVITY], user);
},
GET_EXPANSION_SEQUENCE_SEQ_JSON: () => true,
GET_EXTENSIONS: () => true,
GET_EXTERNAL_EVENTS: () => true,
GET_EXTERNAL_EVENT_TYPE_BY_SOURCE: () => true,
GET_MODELS: () => true,
Expand Down Expand Up @@ -858,6 +857,7 @@ const queryPermissions: Record<GQLKeys, (user: User | null, ...args: any[]) => b
SUB_EXPANSION_SETS: (user: User | null): boolean => {
return isUserAdmin(user) || getPermission([Queries.EXPANSION_SETS], user);
},
SUB_EXTENSIONS: () => true,
SUB_EXTERNAL_EVENT_TYPES: () => true,
SUB_EXTERNAL_SOURCE: () => true,
SUB_EXTERNAL_SOURCES: () => true,
Expand Down
Loading