Skip to content

Commit 389144d

Browse files
authored
Support Conditional Clicking in Broker Protection (#1141)
* First pass at conditional clicking * Add additional tests * Fix linting issues, remove unneeded operators * Fix types for BuildUrl * Minor refactor, add unit tests * Clean-up error reporting and add test * Refactor subactions to run from routing * Refactor click action error workflow * Flip bit back on integration test * Missed conflict resolution * Add collector to integration tests * Missed one * Adopt additional API changes * Lint fixes
1 parent 22e3eb0 commit 389144d

17 files changed

+592
-59
lines changed

injected/integration-test/broker-protection.spec.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,70 @@ test.describe('Broker Protection communications', () => {
395395
await page.waitForURL((url) => url.hash === '#1-2', { timeout: 2000 });
396396
});
397397

398+
test('conditional clicks - hard-coded success', async ({ page }, workerInfo) => {
399+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
400+
await dbp.enabled();
401+
await dbp.navigatesTo('conditional-clicks.html');
402+
await dbp.receivesAction('conditional-clicks-hard-coded-success.json');
403+
const response = await dbp.collector.waitForMessage('actionCompleted');
404+
405+
dbp.isSuccessMessage(response);
406+
await page.waitForURL((url) => url.hash === '#yes', { timeout: 2000 });
407+
});
408+
409+
test('conditional clicks - hard-coded default', async ({ page }, workerInfo) => {
410+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
411+
await dbp.enabled();
412+
await dbp.navigatesTo('conditional-clicks.html');
413+
await dbp.receivesAction('conditional-clicks-hard-coded-default.json');
414+
const response = await dbp.collector.waitForMessage('actionCompleted');
415+
416+
dbp.isSuccessMessage(response);
417+
await page.waitForURL((url) => url.hash === '#no', { timeout: 2000 });
418+
});
419+
420+
test('conditional clicks - do not throw error on defined (but empty) default', async ({ page }, workerInfo) => {
421+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
422+
await dbp.enabled();
423+
await dbp.navigatesTo('conditional-clicks.html');
424+
await dbp.receivesAction('conditional-clicks-hard-coded-null-default.json');
425+
const response = await dbp.collector.waitForMessage('actionCompleted');
426+
427+
dbp.isSuccessMessage(response);
428+
});
429+
430+
test('conditional clicks - throw error if default is undefined', async ({ page }, workerInfo) => {
431+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
432+
await dbp.enabled();
433+
await dbp.navigatesTo('conditional-clicks.html');
434+
await dbp.receivesAction('conditional-clicks-hard-coded-undefined-default.json');
435+
const response = await dbp.collector.waitForMessage('actionCompleted');
436+
437+
dbp.isErrorMessage(response);
438+
});
439+
440+
test('conditional clicks - interpolated success', async ({ page }, workerInfo) => {
441+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
442+
await dbp.enabled();
443+
await dbp.navigatesTo('conditional-clicks.html');
444+
await dbp.receivesAction('conditional-clicks-interpolated-success.json');
445+
const response = await dbp.collector.waitForMessage('actionCompleted');
446+
447+
dbp.isSuccessMessage(response);
448+
await page.waitForURL((url) => url.hash === '#yes', { timeout: 2000 });
449+
});
450+
451+
test('conditional clicks - interpolated default', async ({ page }, workerInfo) => {
452+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
453+
await dbp.enabled();
454+
await dbp.navigatesTo('conditional-clicks.html');
455+
await dbp.receivesAction('conditional-clicks-interpolated-default.json');
456+
const response = await dbp.collector.waitForMessage('actionCompleted');
457+
458+
dbp.isSuccessMessage(response);
459+
await page.waitForURL((url) => url.hash === '#no', { timeout: 2000 });
460+
});
461+
398462
test('getCaptchaInfo', async ({ page }, workerInfo) => {
399463
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
400464
await dbp.enabled();
@@ -524,6 +588,17 @@ test.describe('Broker Protection communications', () => {
524588
expect(currentUrl).not.toContain('#');
525589
});
526590

591+
test('expectation with conditional subaction', async ({ page }, workerInfo) => {
592+
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
593+
await dbp.enabled();
594+
await dbp.navigatesTo('expectation-actions.html');
595+
await dbp.receivesAction('expectation-actions-conditional-subaction.json');
596+
const response = await dbp.collector.waitForMessage('actionCompleted');
597+
598+
dbp.isSuccessMessage(response);
599+
await page.waitForURL((url) => url.hash === '#2', { timeout: 2000 });
600+
});
601+
527602
test.describe('retrying', () => {
528603
test('retrying a click', async ({ page }, workerInfo) => {
529604
const dbp = BrokerProtectionPage.create(page, workerInfo.project.use);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "click",
5+
"id": "1",
6+
"choices": [
7+
{
8+
"condition": {
9+
"left": "40",
10+
"operation": ">=",
11+
"right": "45"
12+
},
13+
"elements": [
14+
{ "type": "button", "selector": ".//a[text()='Yes']" }
15+
]
16+
}
17+
],
18+
"default": {
19+
"elements": [
20+
{ "type": "button", "selector": ".//a[text()='No']" }
21+
]
22+
}
23+
}
24+
}
25+
}
26+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "click",
5+
"id": "1",
6+
"choices": [
7+
{
8+
"condition": {
9+
"left": "40",
10+
"operation": ">=",
11+
"right": "45"
12+
},
13+
"elements": [
14+
{ "type": "button", "selector": ".//a[text()='Yes']" }
15+
]
16+
}
17+
],
18+
"default": null
19+
}
20+
}
21+
}
22+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "click",
5+
"id": "1",
6+
"choices": [
7+
{
8+
"condition": {
9+
"left": "50",
10+
"operation": ">=",
11+
"right": "45"
12+
},
13+
"elements": [
14+
{ "type": "button", "selector": ".//a[text()='Yes']" }
15+
]
16+
}
17+
],
18+
"default": {
19+
"elements": [
20+
{ "type": "button", "selector": ".//a[text()='No']" }
21+
]
22+
}
23+
}
24+
}
25+
}
26+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "click",
5+
"id": "1",
6+
"retry": {
7+
"environment": "web",
8+
"interval": { "ms": 1000 },
9+
"maxAttempts": 1
10+
},
11+
"choices": [
12+
{
13+
"condition": {
14+
"left": "40",
15+
"operation": ">=",
16+
"right": "45"
17+
},
18+
"elements": [
19+
{ "type": "button", "selector": ".//a[text()='Yes']" }
20+
]
21+
}
22+
]
23+
}
24+
}
25+
}
26+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "click",
5+
"id": "1",
6+
"choices": [
7+
{
8+
"condition": {
9+
"left": "${age}",
10+
"operation": ">=",
11+
"right": "45"
12+
},
13+
"elements": [
14+
{ "type": "button", "selector": ".//a[text()='Yes']" }
15+
]
16+
}
17+
],
18+
"default": {
19+
"elements": [
20+
{ "type": "button", "selector": ".//a[text()='No']" }
21+
]
22+
}
23+
},
24+
"data": {
25+
"userProfile": {
26+
"age": "40"
27+
}
28+
}
29+
}
30+
}
31+
32+
33+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "click",
5+
"id": "1",
6+
"choices": [
7+
{
8+
"condition": {
9+
"left": "${age}",
10+
"operation": ">=",
11+
"right": "45"
12+
},
13+
"elements": [
14+
{ "type": "button", "selector": ".//a[text()='Yes']" }
15+
]
16+
}
17+
],
18+
"default": {
19+
"elements": [
20+
{ "type": "button", "selector": ".//a[text()='No']" }
21+
]
22+
}
23+
},
24+
"data": {
25+
"userProfile": {
26+
"age": "50"
27+
}
28+
}
29+
}
30+
}
31+
32+
33+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"state": {
3+
"action": {
4+
"actionType": "expectation",
5+
"id": "2",
6+
"expectations": [
7+
{
8+
"type": "text",
9+
"selector": "body",
10+
"expect": "How old is John Doe?"
11+
}
12+
],
13+
"actions": [
14+
{
15+
"actionType": "click",
16+
"id": "1",
17+
"choices": [
18+
{
19+
"condition": {
20+
"left": "${age}",
21+
"operation": ">=",
22+
"right": "45"
23+
},
24+
"elements": [
25+
{ "type": "button", "selector": ".//a[text()='Yes']" }
26+
]
27+
}
28+
],
29+
"default": {
30+
"elements": [
31+
{ "type": "button", "selector": ".//a[text()='No']" }
32+
]
33+
}
34+
}
35+
]
36+
},
37+
"data": {
38+
"userProfile": {
39+
"age": "40"
40+
}
41+
}
42+
}
43+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width">
6+
<title>Broker Protection</title>
7+
</head>
8+
<body>
9+
<div class="result">
10+
<h3>Is John Doe over the age of 45?</h3>
11+
<a href="#" class="answer" data-id="yes">Yes</a>
12+
<a href="#" class="answer" data-id="no">No</a>
13+
</div>
14+
<script>
15+
document.querySelectorAll('.answer').forEach(answer =>
16+
answer.addEventListener('click', function(e) {
17+
e.preventDefault();
18+
const id = this.getAttribute('data-id');
19+
20+
window.location.hash = id;
21+
this.removeEventListener('click', arguments.callee);
22+
})
23+
);
24+
</script>
25+
</body>
26+
</html>

injected/integration-test/test-pages/broker-protection/pages/expectation-actions.html

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@
88
<body>
99
<div class="result">
1010
How old is John Doe?
11-
<a href="#" class="view-more" data-id="1">View More</a>
11+
<a href="#" class="view-more" data-id="1">Yes</a>
12+
<a href="#" class="view-more" data-id="2">No</a>
1213
</div>
1314
<script>
14-
document.querySelector('.view-more').addEventListener('click', function(e) {
15-
e.preventDefault();
16-
const id = this.getAttribute('data-id');
15+
document.querySelectorAll('.view-more').forEach(viewMore =>
16+
viewMore.addEventListener('click', function(e) {
17+
e.preventDefault();
18+
const id = this.getAttribute('data-id');
1719

18-
window.location.hash = id;
19-
this.removeEventListener('click', arguments.callee);
20-
})
20+
window.location.hash = id;
21+
this.removeEventListener('click', arguments.callee);
22+
})
23+
);
2124
</script>
2225
</body>
2326
</html>

0 commit comments

Comments
 (0)