-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathactions-test.js
More file actions
381 lines (332 loc) · 11.6 KB
/
actions-test.js
File metadata and controls
381 lines (332 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/**
* Copyright IBM Corp. 2015, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
// @ts-check
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import Tokens from 'nomad-ui/tests/pages/settings/tokens';
import a11yAudit from 'nomad-ui/tests/helpers/a11y-audit';
import Actions from 'nomad-ui/tests/pages/jobs/job/actions';
import { triggerEvent, visit, click } from '@ember/test-helpers';
import faker from 'nomad-ui/mirage/faker';
module('Acceptance | actions', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.beforeEach(async function () {
faker.seed(1);
window.localStorage.clear();
server.create('agent');
server.create('node-pool');
server.create('node');
const actionsJob = server.create('job', {
createAllocations: true,
resourceSpec: Array(2).fill('M: 257, C: 500'),
groupAllocCount: 5,
groupTaskCount: 2,
shallow: false,
name: 'actionable-job',
id: 'actionable-job',
namespaceId: 'default',
type: 'service',
activeDeployment: false,
noDeployments: true,
allocStatusDistribution: {
running: 1,
},
noFailedPlacements: true,
status: 'running',
withActions: true,
});
// A third task group in the Actions job with a single task/alloc
const actionsGroup = server.create('task-group', {
jobId: actionsJob.id,
name: 'actionable-group',
taskCount: 1,
});
// make sure the allocation generated by that group is running
server.schema.allocations.findBy({ taskGroup: actionsGroup.name }).update({
clientStatus: 'running',
});
// Set its task state to running
server.schema.allocations
.all()
.filter((x) => x.taskGroup === actionsGroup.name)
.models[0].taskStates.models[0]?.update({
state: 'running',
});
});
test('Actions show up on the Job Index page, permissions allowing', async function (assert) {
assert.expect(8);
let managementToken = server.create('token', {
type: 'management',
name: 'Management Token',
});
let clientReaderToken = server.create('token', {
type: 'client',
name: "N. O'DeReader",
});
const allocExecPolicy = server.create('policy', {
id: 'alloc-exec',
rules: `
namespace "*" {
policy = "read"
capabilities = ["list-jobs", "alloc-exec", "read-logs"]
}
`,
rulesJSON: {
Namespaces: [
{
Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'],
Name: '*',
},
],
},
});
let allocExecToken = server.create('token', {
type: 'client',
name: 'Alloc Exec Token',
policyIds: [allocExecPolicy.id],
});
await Actions.visitIndex({ id: 'actionable-job' });
// no actions dropdown by default
assert.notOk(Actions.hasTitleActions, 'No actions dropdown by default');
await Tokens.visit();
const { secretId } = managementToken;
await Tokens.secret(secretId).submit();
await Actions.visitIndex({ id: 'actionable-job' });
assert.ok(
Actions.hasTitleActions,
'Management token sees actions dropdown'
);
assert.ok(Actions.taskRowActions.length, 'Task row has actions dropdowns');
await a11yAudit(assert);
// Sign out and sign back in as a token without alloc exec
await Tokens.visit();
await Tokens.clear();
await Tokens.secret(clientReaderToken.secretId).submit();
await Actions.visitIndex({ id: 'actionable-job' });
assert.notOk(
Actions.hasTitleActions,
'Basic client token does not see actions dropdown'
);
assert.notOk(
Actions.taskRowActions.length,
'Basic client token does not see task row actions dropdowns'
);
// Sign out and sign back in as a token with alloc exec
await Tokens.visit();
await Tokens.clear();
await Tokens.secret(allocExecToken.secretId).submit();
await Actions.visitIndex({ id: 'actionable-job' });
assert.ok(
Actions.hasTitleActions,
'Alloc exec token sees actions dropdown'
);
assert.ok(
Actions.taskRowActions.length,
'Alloc exec token sees task row actions dropdowns'
);
});
// Running actions test
test('Running actions and notifications', async function (assert) {
assert.expect(20);
let managementToken = server.create('token', {
type: 'management',
name: 'Management Token',
});
await Tokens.visit();
const { secretId } = managementToken;
await Tokens.secret(secretId).submit();
await Actions.visitIndex({ id: 'actionable-job' });
assert.ok(
Actions.hasTitleActions,
'Management token sees actions dropdown'
);
// Open the dropdown
await Actions.titleActions.click();
assert.equal(Actions.titleActions.expandedValue, 'true');
assert.equal(
Actions.titleActions.actions.length,
5,
'5 actions show up in the dropdown'
);
assert.equal(
Actions.titleActions.multiAllocActions.length,
4,
'4 actions in the dropdown have multiple allocs to run against'
);
assert.equal(
Actions.titleActions.singleAllocActions.length,
1,
'1 action in the dropdown has a single alloc to run against'
);
assert.equal(
Actions.titleActions.multiAllocActions[0].button[0].expanded,
'false',
"The first action's dropdown is not expanded"
);
assert.notOk(
Actions.titleActions.multiAllocActions[0].showsDisclosureContent,
"The first action's dropdown subcontent does not yet exist"
);
await Actions.titleActions.actions[0].click();
assert.equal(
Actions.titleActions.multiAllocActions[0].button[0].expanded,
'true',
"The first action's dropdown is expanded"
);
assert.ok(
Actions.titleActions.multiAllocActions[0].showsDisclosureContent,
"The first action's dropdown subcontent exists"
);
// run on a random alloc
await Actions.titleActions.multiAllocActions[0].subActions[0].click();
assert.ok(Actions.flyout.isPresent);
assert.equal(
Actions.flyout.instances.length,
1,
'A sidebar instance pops up upon running an action'
);
assert.ok(
Actions.flyout.instances[0].code.includes('Message Received'),
'The instance contains the message from the action'
);
assert.ok(
Actions.flyout.instances[0].statusBadge.includes('Complete'),
'The instance contains the status of the action'
);
await Actions.flyout.close();
// Type the escape key: the Helios dropdown doesn't automatically close on click-away events
// as defined by clickable in the page object here, so we should explicitly make sure it's closed.
await triggerEvent('.job-page-header .actions-dropdown', 'keyup', {
key: 'Escape',
});
assert.notOk(Actions.flyout.isPresent);
assert.equal(Actions.titleActions.expandedValue, 'false');
await Actions.titleActions.click();
await Actions.titleActions.multiAllocActions[0].button[0].click();
await Actions.titleActions.multiAllocActions[0].subActions[1].click();
assert.ok(Actions.flyout.isPresent);
// 2 assets, the second of which has multiple peer allocs within it
assert.equal(
Actions.flyout.instances.length,
2,
'Running on all allocs in the group (1) results in 2 total instances'
);
assert.ok(
Actions.flyout.instances[0].hasPeers,
'The first instance has peers'
);
assert.notOk(
Actions.flyout.instances[1].hasPeers,
'The second instance does not have peers'
);
await Actions.flyout.close();
// Type the escape key: the Helios dropdown doesn't automatically close on click-away events
// as defined by clickable in the page object here, so we should explicitly make sure it's closed.
await triggerEvent('.job-page-header .actions-dropdown', 'keyup', {
key: 'Escape',
});
await Actions.titleActions.click();
await Actions.titleActions.singleAllocActions[0].button[0].click();
assert.equal(
Actions.flyout.instances.length,
3,
'Running on an orphan alloc results in 1 further action instance'
);
});
test('Running actions from a task row', async function (assert) {
let managementToken = server.create('token', {
type: 'management',
name: 'Management Token',
});
await Tokens.visit();
const { secretId } = managementToken;
await Tokens.secret(secretId).submit();
await Actions.visitAllocs({ id: 'actionable-job' });
// Get the number of rows; each of them should have an actions dropdown
const job = server.schema.jobs.find('actionable-job');
const numberOfTaskRows = server.schema.allocations
.all()
.models.filter((a) => a.jobId === job.name)
.map((a) => a.taskStates.models)
.flat().length;
assert.equal(
Actions.taskRowActions.length,
numberOfTaskRows,
'Each task row has an actions dropdown'
);
await Actions.taskRowActions[0].click();
assert.equal(
Actions.taskRowActions[0].actions.length,
1,
'Actions within a task row actions dropdown are shown'
);
await Actions.taskRowActions[0].actions[0].click();
assert.ok(Actions.flyout.isPresent);
assert.equal(
Actions.flyout.instances.length,
1,
'A sidebar instance pops up upon running an action'
);
assert.ok(
Actions.flyout.instances[0].code.includes('Message Received'),
'The instance contains the message from the action'
);
});
test('Actions flyout gets dynamic actions list', async function (assert) {
assert.expect(8);
let managementToken = server.create('token', {
type: 'management',
name: 'Management Token',
});
await Tokens.visit();
const { secretId } = managementToken;
await Tokens.secret(secretId).submit();
await Actions.visitIndex({ id: 'actionable-job' });
// Run an action to open the flyout; observe the dropdown there
await Actions.titleActions.click();
await Actions.titleActions.singleAllocActions[0].button[0].click();
// Is flyout open?
assert.ok(Actions.flyout.isPresent, 'Flyout is open');
// Is there a dropdown in the flyout?
assert.ok(Actions.flyout.actions.isPresent, 'Flyout has actions dropdown');
// Close the flyout go to the Jobs page
await Actions.flyout.close();
await visit('/jobs');
assert.notOk(Actions.flyout.isPresent, 'Flyout is closed');
// Global button should be present
assert.ok(Actions.globalButton.isPresent, 'Global button is present');
// click it
await Actions.globalButton.click();
// actions flyout should be open
assert.ok(Actions.flyout.isPresent, 'Flyout is open');
// it shouldn't have a dropdown in it
assert.notOk(
Actions.flyout.actions.isPresent,
'Flyout has no actions dropdown'
);
await Actions.flyout.close();
// head back into the job, and into a task
await Actions.visitIndex({ id: 'actionable-job' });
await click('[data-test-task-group="actionable-group"] a');
await click('.task-name');
// Click global button
await Actions.globalButton.click();
// Dropdown present
assert.ok(
Actions.flyout.actions.isPresent,
'Flyout has actions dropdown on task page'
);
// Clear finished actions and take a snapshot
await click('button[data-test-clear-finished-actions]');
// Close flyout; global button is no longer present
await Actions.flyout.close();
assert.notOk(
Actions.globalButton.isPresent,
'Global button is not present after flyout close'
);
});
});