Skip to content

Commit 6f94953

Browse files
JosephVolosinpsubram3AaronPlave
authored
Activity Directive Builder (#1900)
Introduces the Activity Directive Builder! Co-authored-by: JosephVolosin <joseph.p.volosin@nasa.gov> Co-authored-by: psubram3 <pranav.subramanian@nasa.gov> Co-authored-by: AaronPlave <aaronplave@gmail.com>
1 parent 0aa5f2a commit 6f94953

27 files changed

Lines changed: 718 additions & 84 deletions

e2e-tests/fixtures/Plan.ts

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export class Plan {
108108
this.updatePage(page);
109109
}
110110

111-
async addActivity(name: string = 'GrowBanana') {
111+
async addActivityByDragAndDrop(name: string = 'GrowBanana') {
112112
// Ensure Activity Directives Table panel is visible for verification later
113113
if (!(await this.panelActivityDirectivesTable.isVisible())) {
114114
await this.showPanel(PanelNames.ACTIVITY_DIRECTIVES_TABLE);
@@ -132,6 +132,92 @@ export class Plan {
132132
await activityRow.scrollIntoViewIfNeeded();
133133
// Use dragTo for the drag operation
134134
await activityListItem.dragTo(activityRow, { timeout: 10000 });
135+
// Directive builder should have appeared
136+
const activityDirectiveBuilder = this.page.getByText('Activity Directive Builder');
137+
await expect(activityDirectiveBuilder).toBeVisible();
138+
// Select the proper activity type
139+
await this.page.getByLabel('manual-types').getByText(name).click();
140+
// Create the activity
141+
await this.page.getByRole('button', { name: 'Create Activity Directive' }).click();
142+
await this.waitForToast('Activity Directive Created Successfully');
143+
// Verify at least one activity with this name exists in the table
144+
await expect(this.panelActivityDirectivesTable.getByRole('row', { name }).first()).toBeVisible({ timeout: 10000 });
145+
}
146+
147+
async addActivityByGenericButton(name: string = 'GrowBanana') {
148+
// Ensure Activity Directives Table panel is visible for verification later
149+
if (!(await this.panelActivityDirectivesTable.isVisible())) {
150+
await this.showPanel(PanelNames.ACTIVITY_DIRECTIVES_TABLE);
151+
}
152+
await this.showPanel(PanelNames.TIMELINE_ITEMS);
153+
const activityListItem = this.page.locator(`.list-item :text-is("${name}")`);
154+
await expect(activityListItem).toBeVisible();
155+
const activityRow = this.page
156+
.locator('.timeline')
157+
.getByRole('listitem')
158+
.filter({ hasText: 'Activities by Type' })
159+
.first()
160+
.locator('.overlay');
161+
await expect(activityRow).toBeVisible();
162+
// Wait for timeline to finish loading before attempting drag
163+
await this.waitForTimelineLoading();
164+
// Click on activity item first to ensure Svelte drag listeners are initialized
165+
await activityListItem.click();
166+
// Scroll elements into view
167+
await activityListItem.scrollIntoViewIfNeeded();
168+
await activityRow.scrollIntoViewIfNeeded();
169+
// Open builder
170+
const addActivityButton = await this.page.getByRole('button', { name: 'Add Activity' });
171+
await addActivityButton.scrollIntoViewIfNeeded();
172+
await addActivityButton.click();
173+
// Directive builder should have appeared
174+
const activityDirectiveBuilder = this.page.getByPlaceholder('Enter an optional name for this directive');
175+
await expect(activityDirectiveBuilder).toBeVisible();
176+
// Select the proper activity type
177+
await this.page.getByLabel('manual-types').getByRole('combobox').click();
178+
await this.page.getByRole('menuitem', { name }).click();
179+
// Create the activity
180+
await this.page.getByRole('button', { name: 'Create Activity Directive' }).click();
181+
await this.waitForToast('Activity Directive Created Successfully');
182+
// Verify at least one activity with this name exists in the table
183+
await expect(this.panelActivityDirectivesTable.getByRole('row', { name }).first()).toBeVisible({ timeout: 10000 });
184+
}
185+
186+
async addActivityByTypeButton(name: string = 'GrowBanana') {
187+
// Ensure Activity Directives Table panel is visible for verification later
188+
if (!(await this.panelActivityDirectivesTable.isVisible())) {
189+
await this.showPanel(PanelNames.ACTIVITY_DIRECTIVES_TABLE);
190+
}
191+
await this.showPanel(PanelNames.TIMELINE_ITEMS);
192+
const activityListItem = this.page.locator(`.list-item :text-is("${name}")`);
193+
await expect(activityListItem).toBeVisible();
194+
const activityRow = this.page
195+
.locator('.timeline')
196+
.getByRole('listitem')
197+
.filter({ hasText: 'Activities by Type' })
198+
.first()
199+
.locator('.overlay');
200+
await expect(activityRow).toBeVisible();
201+
// Wait for timeline to finish loading before attempting drag
202+
await this.waitForTimelineLoading();
203+
// Click on activity item first to ensure Svelte drag listeners are initialized
204+
await activityListItem.click();
205+
// Scroll elements into view
206+
await activityListItem.scrollIntoViewIfNeeded();
207+
await activityRow.scrollIntoViewIfNeeded();
208+
// Open builder
209+
await this.page.getByText(name).hover();
210+
const addActivityButton = await this.page.getByRole('button', { name: `AddActivity-${name}` });
211+
await addActivityButton.scrollIntoViewIfNeeded();
212+
await addActivityButton.click();
213+
// Directive builder should have appeared
214+
const activityDirectiveBuilder = this.page.getByPlaceholder('Enter an optional name for this directive');
215+
await expect(activityDirectiveBuilder).toBeVisible();
216+
// Select the proper activity type
217+
await this.page.getByLabel('manual-types').getByRole('combobox').click();
218+
await this.page.getByRole('menuitem', { name }).click();
219+
// Create the activity
220+
await this.page.getByRole('button', { name: 'Create Activity Directive' }).click();
135221
await this.waitForToast('Activity Directive Created Successfully');
136222
// Verify at least one activity with this name exists in the table
137223
await expect(this.panelActivityDirectivesTable.getByRole('row', { name }).first()).toBeVisible({ timeout: 10000 });

e2e-tests/tests/plan-activities.test.ts

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,48 @@ test.afterAll(async () => {
1919
});
2020

2121
test.describe.serial('Plan Activities', () => {
22+
test('Creating an activity directive via. drag-and-drop', async () => {
23+
await setup.plan.addActivityByDragAndDrop('ParameterTest');
24+
// TODO: Check that timeline has the activity now?
25+
});
26+
27+
test('Creating an activity directive via. generic button', async () => {
28+
await setup.plan.addActivityByGenericButton('ParameterTest');
29+
});
30+
31+
test('Creating an activity directive via. activity type button', async () => {
32+
await setup.plan.addActivityByTypeButton('ParameterTest');
33+
});
34+
2235
test('Deleting an activity directive with another directive anchored to it should and selecting re-anchor to plan should re-anchor to plan', async () => {
23-
await setup.plan.addActivity('GrowBanana');
24-
await setup.plan.addActivity('PickBanana');
25-
await setup.plan.addActivity('ThrowBanana');
36+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
37+
await setup.plan.addActivityByDragAndDrop('PickBanana');
38+
await setup.plan.addActivityByDragAndDrop('ThrowBanana');
39+
40+
await setup.page.pause();
41+
2642
await setup.plan.panelActivityDirectivesTable.getByRole('row', { name: 'PickBanana' }).first().click();
27-
await setup.plan.panelActivityForm
28-
.getByRole('button', { name: 'Is Relative To Another Activity Directive' })
29-
.click();
43+
await setup.plan.panelActivityForm.getByRole('button', { name: 'Is Relative To Another' }).click();
44+
45+
await setup.page.pause();
46+
3047
await setup.plan.selectActivityAnchorByIndex(1);
3148

3249
await setup.plan.panelActivityDirectivesTable.getByRole('row', { name: 'GrowBanana' }).first().click();
3350
await setup.plan.panelActivityDirectivesTable.getByRole('button', { name: 'Delete Activity Directive' }).click();
51+
52+
await setup.page.pause();
53+
3454
await setup.page.locator('.modal-content select').nth(1).selectOption('anchor-plan');
3555
await setup.page.getByRole('button', { name: 'Confirm' }).click();
3656
await setup.plan.panelActivityDirectivesTable
3757
.getByRole('row', { name: 'GrowBanana' })
3858
.waitFor({ state: 'detached', timeout: 2000 });
59+
60+
await setup.page.pause();
61+
3962
await setup.plan.panelActivityDirectivesTable.getByRole('row', { name: 'PickBanana' }).first().click();
40-
await setup.plan.panelActivityForm
41-
.getByRole('button', { name: 'Is Relative To Another Activity Directive' })
42-
.click();
63+
await setup.plan.panelActivityForm.getByRole('button', { name: 'Is Relative To Another' }).click();
4364
await setup.page.waitForFunction(
4465
() => document.querySelector('.anchor-form .selected-display-value')?.innerHTML === 'To Plan',
4566
);
@@ -48,9 +69,9 @@ test.describe.serial('Plan Activities', () => {
4869
});
4970

5071
test('Deleting multiple activity directives but only 1 has a remaining anchored dependent should prompt for just the one with a remaining dependent', async () => {
51-
await setup.plan.addActivity('GrowBanana');
52-
await setup.plan.addActivity('PickBanana');
53-
await setup.plan.addActivity('ThrowBanana');
72+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
73+
await setup.plan.addActivityByDragAndDrop('PickBanana');
74+
await setup.plan.addActivityByDragAndDrop('ThrowBanana');
5475
await setup.plan.panelActivityDirectivesTable.getByRole('row', { name: 'PickBanana' }).first().click();
5576
await setup.plan.panelActivityForm
5677
.getByRole('button', { name: 'Is Relative To Another Activity Directive' })
@@ -77,7 +98,7 @@ test.describe.serial('Plan Activities', () => {
7798
});
7899

79100
test('Setting an input path successfully uploads the corresponding file', async () => {
80-
await setup.plan.addActivity('LineCount');
101+
await setup.plan.addActivityByDragAndDrop('LineCount');
81102

82103
await setFileInputByFilepath(
83104
setup.page,
@@ -94,13 +115,13 @@ test.describe.serial('Plan Activities', () => {
94115
await setup.plan.hoverMenu(setup.plan.navButtonActivityChecking);
95116
await expect(setup.plan.navButtonActivityCheckingMenu).toContainText('0/0 activities checked');
96117
await expect(setup.plan.navButtonActivityCheckingMenu).toContainText('No problems detected');
97-
await setup.plan.addActivity('GrowBanana');
98-
await setup.plan.addActivity('GrowBanana');
118+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
119+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
99120
await setup.plan.waitForActivityCheckingStatus(Status.Complete);
100121
await setup.plan.hoverMenu(setup.plan.navButtonActivityChecking);
101122
await expect(setup.plan.navButtonActivityCheckingMenu).toContainText('2/2 activities checked');
102123
await expect(setup.plan.navButtonActivityCheckingMenu).toContainText('No problems detected');
103-
await setup.plan.addActivity('BakeBananaBread');
124+
await setup.plan.addActivityByDragAndDrop('BakeBananaBread');
104125
await setup.plan.waitForActivityCheckingStatus(Status.Failed);
105126
await setup.plan.hoverMenu(setup.plan.navButtonActivityChecking);
106127
await expect(setup.plan.navButtonActivityCheckingMenu).toContainText('3/3 activities checked');

e2e-tests/tests/plan-activity-presets.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ test.beforeAll(async ({ browser }) => {
77
setup = await setupTest(browser);
88
await setup.plan.goto();
99

10-
await setup.plan.addActivity('GrowBanana');
10+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
1111
await setup.plan.panelActivityDirectivesTable.getByRole('gridcell', { name: 'GrowBanana' }).first().click();
1212

1313
await setup.plan.panelActivityForm.locator('.parameter-base-number input[type="number"]').fill('2');

e2e-tests/tests/plan-expansion.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ test.describe.serial('Plan Expansion', () => {
5252
await setup.plan.createSequenceFilter(sequenceFilterName);
5353
});
5454
test('Sequence Filter can be applied to a plan', async () => {
55-
await setup.plan.addActivity('PeelBanana');
55+
await setup.plan.addActivityByDragAndDrop('PeelBanana');
5656
await setup.plan.showPanel(PanelNames.SIMULATION, true);
5757
await setup.plan.runSimulation();
5858
await setup.plan.showPanel(PanelNames.EXPANSION);

e2e-tests/tests/plan-external-source.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ test.describe.serial('Plan External Sources', () => {
142142
await setupA.page.getByRole('tab', { exact: true, name: 'Events' }).click();
143143
await expect(setupA.page.locator('.list-item').getByText(externalSources.exampleEventType)).toBeVisible();
144144
await setupA.page.locator('.list-item').getByText(externalSources.exampleEventType).first().hover();
145-
await setupA.page.getByLabel(`AddExternalevent-${externalSources.exampleEventType}`).click();
145+
await setupA.page.getByLabel(`FilterExternalevent-${externalSources.exampleEventType}`).click();
146146
await setupA.page.getByRole('menuitem', { name: 'New Row +' }).click();
147147
await expect(
148148
setupA.page.locator('#timeline-0').getByRole('button', { name: externalSources.exampleEventType }),

e2e-tests/tests/plan-merge.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ test.describe.serial('Plan Merge', () => {
1818
const planBranchName = uniqueNamesGenerator({ dictionaries: [adjectives, colors, animals] });
1919

2020
test('Add an activity to the parent plan', async () => {
21-
await setup.plan.addActivity('BiteBanana');
21+
await setup.plan.addActivityByDragAndDrop('BiteBanana');
2222
});
2323

2424
test('Create a branch', async ({ baseURL }) => {
@@ -93,7 +93,7 @@ test.describe.serial('Plan Merge with Deleted Source Plan', () => {
9393
const planBranchName = uniqueNamesGenerator({ dictionaries: [adjectives, colors, animals] });
9494

9595
test('Add an activity to the parent plan', async () => {
96-
await setup.plan.addActivity('GrowBanana');
96+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
9797
});
9898

9999
test('Create a branch', async ({ baseURL }) => {

e2e-tests/tests/plan-model-migration.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ test.beforeAll(async ({ browser, baseURL }) => {
4242
await plans.goto();
4343
await plans.createPlan(plans.planName, modelA);
4444
await plan.goto();
45-
await plan.addActivity('BakeBananaBread');
46-
await plan.addActivity('BiteBanana');
47-
await plan.addActivity('ChangeProducer');
48-
await plan.addActivity('GrowBanana');
49-
await plan.addActivity('LineCount');
50-
await plan.addActivity('PeelBanana');
51-
await plan.addActivity('PickBanana');
45+
await plan.addActivityByDragAndDrop('BakeBananaBread');
46+
await plan.addActivityByDragAndDrop('BiteBanana');
47+
await plan.addActivityByDragAndDrop('ChangeProducer');
48+
await plan.addActivityByDragAndDrop('GrowBanana');
49+
await plan.addActivityByDragAndDrop('LineCount');
50+
await plan.addActivityByDragAndDrop('PeelBanana');
51+
await plan.addActivityByDragAndDrop('PickBanana');
5252
});
5353

5454
test.afterAll(async () => {

e2e-tests/tests/plan-simulation-templates.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ test.beforeAll(async ({ browser }) => {
1010

1111
await setup.plan.showPanel(PanelNames.SIMULATION, true);
1212

13-
await setup.plan.addActivity('child');
13+
await setup.plan.addActivityByDragAndDrop('child');
1414
await setup.page.getByRole('button', { name: 'Simulate' }).click();
1515

1616
await setup.plan.panelSimulation.locator('.parameter-base-number input[type="number"]').first().fill('199');

e2e-tests/tests/plan.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ test.describe.serial('Plan', () => {
127127

128128
test(`Changing to a new plan should clear the selected activity`, async ({ baseURL }) => {
129129
// Create an activity which will be auto selected
130-
await setup.plan.addActivity('GrowBanana');
130+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
131131

132132
// Switch to a new branch and ensure no activity is selected
133133
await setup.plan.createBranch(baseURL);
@@ -137,7 +137,7 @@ test.describe.serial('Plan', () => {
137137
await expect(setup.plan.panelActivityDirectivesTable).toBeVisible();
138138

139139
// Add a new activity
140-
await setup.plan.addActivity('GrowBanana');
140+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
141141

142142
const branchPlanUrlRegex = new RegExp(`${baseURL}/plans/(?<planId>\\d+)`);
143143
const matches = setup.page.url().match(branchPlanUrlRegex);

e2e-tests/tests/scheduling.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ test.describe.serial('Scheduling', () => {
8888

8989
test('Modifying the plan should result in scheduling status marked as out of date', async () => {
9090
await setup.plan.showPanel(PanelNames.TIMELINE_ITEMS);
91-
await setup.plan.addActivity('GrowBanana');
91+
await setup.plan.addActivityByDragAndDrop('GrowBanana');
9292
await setup.plan.showPanel(PanelNames.SCHEDULING_GOALS);
9393
await setup.plan.waitForSchedulingStatus(Status.Modified);
9494
});

0 commit comments

Comments
 (0)