Skip to content

Commit c127424

Browse files
committed
feat: Allow separate navigation, page interaction and saving snapshots
If you need to create multiple snapshots for a more complicated scenario on an SPA, you will want to stay on the same page and do a couple of actions. The objects in the `pages` array need only one of "url", "file" and "wait" parameters to allow creating a sequence of commands.
1 parent 1455694 commit c127424

File tree

5 files changed

+108
-28
lines changed

5 files changed

+108
-28
lines changed

Gruntfile.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ module.exports = function (grunt) {
6868
dest: 'test/snapshots'
6969
},
7070
'single-target.html': {
71-
url: 'http://localhost:8881/test/pages/single-target.html',
71+
url: 'http://localhost:8881/test/pages/single-target.html'
7272
},
7373
others: {
7474
pages: [
@@ -112,6 +112,22 @@ module.exports = function (grunt) {
112112
},
113113
file: 'no-doctype.html',
114114
url: 'http://localhost:8881/test/pages/no-doctype.html'
115+
},
116+
{
117+
url: 'http://localhost:8881/test/pages/dynamic-multiple.html'
118+
},
119+
{
120+
file: 'dynamic-first.html'
121+
},
122+
{
123+
wait: '.second',
124+
file: 'dynamic-second.html'
125+
},
126+
{
127+
wait: '.third'
128+
},
129+
{
130+
file: 'dynamic-third.html'
115131
}
116132
]
117133
}
@@ -139,10 +155,6 @@ module.exports = function (grunt) {
139155
grunt.loadNpmTasks('grunt-selenium-standalone');
140156
grunt.loadTasks(coverage ? 'coverage/tasks' : 'tasks');
141157

142-
grunt.registerTask('default', coverage ?
143-
['jshint', 'clean', 'instrument', 'embedFonts', 'nodeunit',
144-
'storeCoverage', 'makeReport'] :
145-
['jshint', 'clean:tests', 'embedFonts', 'nodeunit']);
146158
grunt.registerTask('default', coverage ? [
147159
'clean', 'eslint', 'instrument',
148160
'selenium_standalone:serverConfig:install',

README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,10 @@ If the sub-task contains a property `pages`, this property is supposed to point
131131

132132
### Parameters
133133

134+
One of the `file`, `url` ans `wait` oarameters has to be present.
135+
134136
#### file
135-
Type: `String` (mandatory)
137+
Type: `String`
136138

137139
Name of the file to write the snapshot to.
138140

@@ -143,8 +145,10 @@ Name of the file to write the snapshot to.
143145
}
144146
```
145147

148+
If it is omitted, the object will save no snashot. It can still change location or wait for some change.
149+
146150
#### url
147-
Type: `String` (mandatory)
151+
Type: `String`
148152

149153
URL to connect the web browser to for taking the snapshot.
150154

@@ -155,6 +159,8 @@ URL to connect the web browser to for taking the snapshot.
155159
}
156160
```
157161

162+
If it is omitted, the object will reuse the previous location. It can wait for a further change and/or save another snapshot.
163+
158164
#### options
159165
Type: `Object` (optional)
160166

@@ -233,6 +239,38 @@ If the selector is prefixed by "!", the waiting waiting will stop, if the node d
233239
}
234240
```
235241

242+
If `wait` is omitted, the task will advance to another item without delay. It can still save a snapshot immediately.
243+
244+
### Parameter Combinations
245+
246+
The following array of objects within the `pages` parameter will change location, make a snapshot immediately to save the server-side pre-rendered content, then another one to see the progress after the first 500 milliseconds and yet another one, once the search form is ready. Then it submits the form by clicking on the "Search" button, waits until the search results are displayed and makes one final snapshot.
247+
248+
```js
249+
{
250+
url: 'http://localhost/app'
251+
file: 'initial.html'
252+
},
253+
{
254+
wait: 500,
255+
file: 'after-500ms.html'
256+
},
257+
{
258+
wait: '#search',
259+
file: 'form-ready.html'
260+
},
261+
{
262+
wait: function (browser) {
263+
return browser.click('#search')
264+
.waitForExist('#results', 1000);
265+
},
266+
file: 'results-shown.html'
267+
}
268+
```
269+
270+
Other Grunt tasks can run later and validate, compare or otherwise process the page content in different stages of the "Search" scenario.
271+
272+
Navigating to other location, interacting with the page, waiting for some effect to show and saving a snapshot can be divided to different objects in the `pages` array. However, at least One of the `file`, `url` ans `wait` oarameters has to be present in ever object.
273+
236274
### Loading
237275

238276
Load the plugin in `Gruntfile.js`:
@@ -292,7 +330,7 @@ grunt.initConfig({
292330
seleniumVersion: '3.7.1',
293331
seleniumDownloadURL: 'http://selenium-release.storage.googleapis.com',
294332
drivers: {
295-
chrome: { // http://chromedriver.storage.googleapis.com/
333+
chrome: {
296334
version: '2.33',
297335
arch: process.arch,
298336
baseURL: 'https://chromedriver.storage.googleapis.com'

tasks/html-dom-snapshot.js

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ module.exports = function (grunt) {
3434
dest: 'snapshots',
3535
force: false
3636
}),
37+
target = this.target,
3738
pages = data.pages || [
3839
Object.assign({
39-
file: this.target
40+
file: target
4041
}, data)
4142
],
4243
client = webdriverio.remote({
@@ -76,12 +77,18 @@ module.exports = function (grunt) {
7677
}
7778

7879
function takeSnapshot(page) {
79-
const url = page.url,
80-
pageOptions = Object.assign({}, options, page.options || {});
81-
grunt.verbose.writeln('Taking a snapshot of ' + url + '...');
80+
const pageOptions = Object.assign({}, options, page.options || {}),
81+
url = page.url,
82+
file = page.file;
83+
var wait = page.wait;
84+
if (url) {
85+
grunt.verbose.writeln('Taking a snapshot of ' + url + '...');
86+
} else {
87+
grunt.verbose.writeln('Preparing the next snapshot...');
88+
}
8289
return client.setViewportSize(pageOptions.viewport)
8390
.then(function () {
84-
return client.url(url);
91+
return url && client.url(url);
8592
})
8693
.then(waitForContent)
8794
.then(function () {
@@ -90,9 +97,8 @@ module.exports = function (grunt) {
9097
.then(saveContent);
9198

9299
function waitForContent() {
93-
var wait = page.wait;
94100
if (!Array.isArray(wait)) {
95-
wait = [wait];
101+
wait = wait ? [wait] : [];
96102
}
97103
return wait.reduce(function (promise, wait) {
98104
return promise.then(function () {
@@ -114,21 +120,23 @@ module.exports = function (grunt) {
114120
}
115121

116122
function saveContent(html) {
117-
const dest = pageOptions.dest,
118-
target = path.join(dest, page.file);
119-
grunt.verbose.writeln('Writing the snapshot to ' + target + '...');
120-
return ensureDirectory(dest)
121-
.then(function () {
122-
return new Promise(function (resolve, reject) {
123-
fs.writeFile(target, pageOptions.doctype + html, function (error) {
124-
if (error) {
125-
reject(error);
126-
} else {
127-
resolve();
128-
}
123+
const dest = pageOptions.dest;
124+
if (file) {
125+
const target = path.join(dest, file);
126+
grunt.verbose.writeln('Writing the snapshot to ' + target + '...');
127+
return ensureDirectory(dest)
128+
.then(function () {
129+
return new Promise(function (resolve, reject) {
130+
fs.writeFile(target, pageOptions.doctype + html, function (error) {
131+
if (error) {
132+
reject(error);
133+
} else {
134+
resolve();
135+
}
136+
});
129137
});
130138
});
131-
});
139+
}
132140
}
133141
}
134142
});

test/pages/dynamic-multiple.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>dynamic-multiple</title>
5+
<script>setTimeout(function () { document.body.classList.add('second');setTimeout(function () { document.body.classList.add('third') }, 100) }, 100)</script>
6+
</head>
7+
<body class="first"></body>
8+
</html>

test/test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,19 @@ exports['html-dom-snapshot'] = {
6565
const pages = readPages('no-doctype.html');
6666
test.equal(pages.expected, pages.actual, 'no-doctype.html');
6767
test.done();
68+
},
69+
70+
'dynamic-multiple': function (test) {
71+
const original = readPage('pages/dynamic-multiple.html'),
72+
first = readPage('snapshots/dynamic-first.html'),
73+
second = readPage('snapshots/dynamic-second.html'),
74+
third = readPage('snapshots/dynamic-third.html');
75+
var expected = original;
76+
test.equal(expected, first, 'dynamic-first.html');
77+
expected = original.replace('<body class="first">', '<body class="first second">');
78+
test.equal(expected, second, 'dynamic-second.html');
79+
expected = original.replace('<body class="first">', '<body class="first second third">');
80+
test.equal(expected, third, 'dynamic-third.html');
81+
test.done();
6882
}
6983
};

0 commit comments

Comments
 (0)