-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathfluentDsl.js
181 lines (155 loc) · 6.63 KB
/
fluentDsl.js
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
/**
* An example of a fluent custom DSL to structure your tests for documentation purposes more easily into usecases and scenarios
* and to use the full power of scenarioo more easily in your e2e tests.
*
* This is the most powerful DSL that is most easy to extend to additional scenarioo features and most easy to use in your e2e-tests.
*
* This DSL provides the following additional features:
*
* - more easy to set additional information on scenarios and usecases (descriptions, labels, ...)
* - ensure to write step with screenshot at end of each scenario (on passed and failed tests, which is configurable)
* - ensure to validate labels against defined labels in configuration
*
* That is why we propose to use this DSL in real projects as a blueprint starting point for your own e2e-test DSL.
*/
import scenarioo from '../scenarioo-js';
/**
* Configuration 'scenarioo.fluentDslConfig' to define config for fluent DSL with default values.
*/
export var config = {
/**
* Define all the allowed labels that can be applied on use cases, as key value-pairs, undefined labels will fail when set on a use case.
*
* key (=property name): the unique label name
* value: a description of the label
*/
useCaseLabels: {},
/**
* Define all the allowed labels that can be applied on scenarios, as key-value-pairs, undefined labels will fail when set on a scenario.
*
* key (=property name): the unique label name
* value: a description of the label
*/
scenarioLabels: {},
/**
* Define all the allowed labels that can be applied on steps, as key-value-pairs, undefined labels will fail when set on a step.
*
* key (=property name): the unique label name
* value: a description of the label
*/
stepLabels: {}
};
export function useCase(name) {
var description, labels, pendingMessage;
return {
description: function (d) {
description = d;
return this;
},
labels: function (l) {
labels = l;
return this;
},
// here you would have to put more functions to support setting more documentation properties, that you can set on use cases.
pending: function (message) {
pendingMessage = message;
return this;
},
describe: describeUseCase.bind(undefined, describe),
xdescribe: describeUseCase.bind(undefined, xdescribe),
fdescribe: describeUseCase.bind(undefined, fdescribe)
};
function describeUseCase(jasmineDescribeFunction, describeCallbackFunction) {
return jasmineDescribeFunction(name, function () {
if (pendingMessage) {
// Use jasmine pending to put pending describe blocks as pending
pending(pendingMessage);
}
beforeAll(function () {
validateLabels('useCase', config.useCaseLabels, labels);
scenarioo.getUseCaseContext().setDescription(description);
scenarioo.getUseCaseContext().addLabels(labels);
});
/**
* This is needed in any case (!!) to ensure that the last step (whatever is configured to be saved as last step)
* is properly written before the spec execution ends.
*/
afterEach(scenarioo.saveLastStep);
return describeCallbackFunction();
});
}
}
export function scenario(name) {
var description = '';
var labels, pendingMessage;
return {
description: function (d) {
description = d;
return this;
},
labels: function (l) {
labels = l;
return this;
},
// here you would have to put more functions to support setting more documentation properties, that you can set on scenarios.
pending: function (message) {
pendingMessage = message;
return this;
},
it: describeScenario.bind(undefined, it),
xit: describeScenario.bind(undefined, xit),
fit: describeScenario.bind(undefined, fit)
};
function describeScenario(jasmineItFunction, itCallbackFunction) {
if (pendingMessage) {
// Since there is a known bug with using pending for it blocks and asynchronous protractor tests,
// we implemented our own pending workaround, still using xit here.
// See https://github.com/angular/protractor/issues/2454
var spec = xit(name, executeCallback);
spec.pend(pendingMessage);
return spec;
}
else {
jasmineItFunction(name, executeCallback);
}
function executeCallback() {
validateLabels('scenario', config.scenarioLabels, labels);
scenarioo.getScenarioContext().setDescription(description);
scenarioo.getScenarioContext().addLabels(labels);
return itCallbackFunction();
}
}
}
/*
* Save a step, also validates the passed labels according to `scenarioo.fluentDslConfig`.
*
* Call this in your e2e test functions whenever you want scenarioo to report a step (with screen shot and metadata, etc.),
* or even better, hide this calls in your page objects or hook directly into protractor methods to do a step on each important interaction.
*
* @param {string} [stepCaption] - optional description text for the step to be recorded, will be displayed in `title` field of a step in scenarioo.
* @param {object} [additionalProperties]
* @param {string[]} [additionalProperties.labels] - array of strings, labels are special keywords to label steps that have something in common.
* @param {object[]} [additionalProperties.screenAnnotations] - screenAnnotations are special objects to highlight rectangular areas in the screenshot and attach additional documentation data tot his areas (e.g. for clicked elements, or text typed by the user, etc.)
* @returns {Promise} The returned promise will resolve to an object containing the saved step object, the path to the step xml file as well as the path to the screenshot file
**/
export function step(stepCaption, additionalProperties) {
if (additionalProperties && additionalProperties.labels) {
validateLabels('step', config.stepLabels, additionalProperties.labels);
}
return scenarioo.saveStep(stepCaption, additionalProperties);
}
function validateLabels(scopeText, definedLabels, labels) {
if (labels) {
labels.forEach(function (label) {
if (!definedLabels[label]) {
fail('Label "' + label + '" is not defined in your project as a valid label for ' + scopeText + '. Please use `scenarioo.fluentDslConfig.' + scopeText + 'Labels` to define your labels. Currently defined labels allowed in a ' + scopeText + ' are: ' + JSON.stringify(definedLabels));
}
});
}
}
// just for backwards compatibility, for those projects already using these globals.
// you should instead import it from library root (index --> scenarioo object).
global.scenariooDslConfig = config;
global.useCase = useCase;
global.scenario = scenario;
global.step = step;