Skip to content
This repository was archived by the owner on Sep 3, 2022. It is now read-only.

Commit 3ca3ea8

Browse files
author
King Long Tse
authored
Added network request comparison to e2e test (#157)
* tests now compare recorded network requests made by analytics.js * renamed codecept files because they actually runs in JS despite filenames are .ts * added esModuleInterop=true compiler flag and updated import syntax * added helper module to preprocess the HAR files so that the tests can use assert.deepEqual to compare them and ignore any irrelevant differences
1 parent 8d34568 commit 3ca3ea8

17 files changed

+1451
-22
lines changed

Makefile

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,8 @@ build: clean install
5151
.PHONY: build
5252

5353
# Remove temporary files and build artifacts.
54-
clean:
54+
clean: test-e2e-clean
5555
rm -rf *.log coverage build
56-
rm -f ./test-e2e/static/analytics.js
5756
.PHONY: clean
5857

5958
# Remove temporary files, build artifacts, and vendor dependencies.
@@ -81,6 +80,10 @@ test-browser: build
8180
test: lint test-browser
8281
.PHONY: test
8382

83+
###
84+
# E2E tests
85+
###
86+
8487
# Commands to start/stop devServer for e2e tests
8588
start_dev_server = (yarn ts-node ./test-e2e/devServer.ts)
8689
stop_dev_server = (pkill SIGTERM ajs-test-e2e-dev-server)
@@ -91,14 +94,34 @@ start-dev-server:
9194
stop-dev-server:
9295
$(call stop_dev_server) || true
9396

94-
# Run e2e tests
95-
test-e2e: install stop-dev-server
97+
test-e2e-clean:
98+
rm -f ./test-e2e/static/analytics.js
99+
rm -rf ./test-e2e/output
100+
rm -rf ./test-e2e/staging
101+
102+
# Run codecept tests
103+
test-codecept: install stop-dev-server
96104
yarn ts-node ./test-e2e/devServer.ts &
97105
rm -rf ./test-e2e/output
98106
rm -rf ./test-e2e/staging
99107
mkdir ./test-e2e/staging
100108
yarn wait-on http://localhost:8000 && npx codeceptjs run --steps
101109
$(call stop_dev_server)
110+
.PHONY: test-codecept
111+
112+
# Compare recorded network requests from analytics.js against reference requests
113+
# network requests are captured during the codecept tests
114+
test-requests:
115+
TS_NODE_COMPILER_OPTIONS='{"esModuleInterop":true}' yarn mocha -r ts-node/register ./test-e2e/requests.test.ts
116+
.PHONY: test-requests
117+
118+
# Run e2e tests
119+
test-e2e: test-codecept test-requests
102120
.PHONY: test-e2e
103121

122+
# Update the reference data by replacing it with newly generated *.har files in staging directory
123+
test-e2e-update: test-codecept
124+
rm -f ./test-e2e/reference/*.har
125+
cp ./test-e2e/staging/*.har ./test-e2e/reference/
126+
104127
.DEFAULT_GOAL = test

codecept.conf.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { setHeadlessWhen } = require('@codeceptjs/configure');
55
setHeadlessWhen(process.env.HEADLESS);
66

77
exports.config = {
8-
tests: './test-e2e/*.test.ts',
8+
tests: './test-e2e/**/*.codecept.js',
99
output: './test-e2e/output',
1010
helpers: {
1111
Puppeteer: {
@@ -29,7 +29,7 @@ exports.config = {
2929
}
3030
},
3131
include: {
32-
I: './test-e2e/steps_file.ts'
32+
I: './test-e2e/steps_file.js'
3333
},
3434
bootstrap: null,
3535
mocha: {},

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@
6666
"@segment/analytics.js-integration": "^3.2.1",
6767
"@segment/eslint-config": "^4.0.0",
6868
"@types/express": "^4.17.6",
69+
"@types/lodash": "^4.14.155",
70+
"@types/mocha": "^7.0.2",
6971
"@types/node": "^14.0.6",
7072
"@types/node-fetch": "^2.5.7",
7173
"browserify": "13.0.0",
@@ -95,6 +97,7 @@
9597
"karma-spec-reporter": "0.0.26",
9698
"karma-summary-reporter": "^1.5.0",
9799
"lint-staged": "^7.2.0",
100+
"lodash": "^4.17.15",
98101
"mocha": "^2.2.5",
99102
"node-fetch": "^2.6.0",
100103
"np": "^3.0.4",
File renamed without changes.

test-e2e/har/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test:
2+
TS_NODE_COMPILER_OPTIONS='{"esModuleInterop":true}' yarn mocha -r ts-node/register test-e2e/har/*.test.ts

test-e2e/har/har.test.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import 'mocha';
2+
import assert from 'assert';
3+
import { preprocessHarEntries, preprocess, trackingAPIComparisonSchema, HarEntry} from './har'
4+
import fs from 'fs';
5+
import path from 'path';
6+
7+
describe('preprocessHarEntries', () => {
8+
// Recordings 1 & 2 were generated on the same website using the same ajs, at different time
9+
// Recording 3 was generated on the same website as the first two, using a different AJS configuration that
10+
// produces a different tracking api payload
11+
12+
it('should return true for identical recordings', () => {
13+
const a = preprocessHarEntries(fs.readFileSync(path.join(__dirname, 'test_data', 'recording1.har'), 'utf8'));
14+
const b = preprocessHarEntries(fs.readFileSync(path.join(__dirname, 'test_data', 'recording1.har'), 'utf8'));
15+
assert.deepEqual(a, b)
16+
});
17+
18+
it('should return true for recordings generated by the same AJS on the same website', () => {
19+
const a = preprocessHarEntries(fs.readFileSync(path.join(__dirname, 'test_data', 'recording1.har'), 'utf8'));
20+
const b = preprocessHarEntries(fs.readFileSync(path.join(__dirname, 'test_data', 'recording2.har'), 'utf8'));
21+
assert.deepEqual(a, b)
22+
});
23+
24+
it('should return false for recordings generated by different AJS configurations on the same website', () => {
25+
const a = preprocessHarEntries(fs.readFileSync(path.join(__dirname, 'test_data', 'recording1.har'), 'utf8'));
26+
const b = preprocessHarEntries(fs.readFileSync(path.join(__dirname, 'test_data', 'recording3.har'), 'utf8'));
27+
assert.notDeepEqual(a, b)
28+
});
29+
});
30+
31+
describe('preprocess', () => {
32+
it('should ignore user-agent headers', () => {
33+
const a = {request: {headers: [{name: 'user-agent', value: 'foo'}]}} as HarEntry
34+
const b = {request: {headers: [{name: 'user-agent', value: 'bar'}]}} as HarEntry
35+
assert.deepEqual(
36+
preprocess(a, trackingAPIComparisonSchema),
37+
preprocess(b, trackingAPIComparisonSchema)
38+
)
39+
})
40+
41+
it('should ignore user-agent header #2', () => {
42+
const a = {request: {headers: [{name: 'user-agent', value: 'foo'}]}} as HarEntry
43+
const b = {request: {headers: [{name: 'user-agent', value: 'foo'}]}} as HarEntry
44+
assert.deepEqual(
45+
preprocess(a, trackingAPIComparisonSchema),
46+
preprocess(b, trackingAPIComparisonSchema)
47+
)
48+
})
49+
50+
it('compares object using a schema that contains properties we want to ignore / only care about existene', () => {
51+
const a = { b: 'foo', c: { d: 1 } };
52+
const b = { a: 1, b: 2, c: { d: 1 } };
53+
const schema = { ignored: ['a'], exists: ['b'] };
54+
assert.deepEqual(
55+
preprocess(a, schema),
56+
preprocess(b, schema)
57+
)
58+
})
59+
60+
describe('ignore', () => {
61+
it('should ignore top-level properties', () => {
62+
const a = { a: 1, b: 2, c: { d: 1 } };
63+
const b = { a: 1, b: 'foobar', c: { d: 1 } };
64+
const schema = { ignored: ['b'] };
65+
assert.deepEqual(
66+
preprocess(a, schema),
67+
preprocess(b, schema)
68+
)
69+
})
70+
71+
it('should ignore non top-level properties', () => {
72+
const a = { a: 1, b: 2, c: { d: 1 } };
73+
const b = { a: 1, b: 2, c: { d: 123 } };
74+
const schema = { ignored: ['c.d'] };
75+
assert.deepEqual(
76+
preprocess(a, schema),
77+
preprocess(b, schema)
78+
)
79+
})
80+
81+
it('should ignored c.d but not entire c', () => {
82+
const a = { b: 2, c: { d: 2 } };
83+
const b = { a: 1, b: 2, c: { d: 1 } };
84+
const schema = { ignored: ['a', 'c.d'] };
85+
assert.deepEqual(
86+
preprocess(a, schema),
87+
preprocess(b, schema)
88+
)
89+
})
90+
91+
it('should ignore c.d but not entire c #2', () => {
92+
const a = { b: 2 }
93+
const b = { a: 1, b: 2, c: { d: 1 } }
94+
const schema = { ignored: ['a', 'c.d'] }
95+
assert.notDeepEqual(
96+
preprocess(a, schema),
97+
preprocess(b, schema)
98+
)
99+
})
100+
101+
it('should ignore entire c and its own properties', () => {
102+
const a = { a: 1, b: 2, c: { g: 1 } }
103+
const b = { a: 1, b: 2, c: { d: 1, e: 2, f: {} } }
104+
const schema = { ignored: ['c'] }
105+
assert.deepEqual(
106+
preprocess(a, schema),
107+
preprocess(b, schema)
108+
)
109+
})
110+
}) // describe ignore
111+
112+
describe('exists', () => {
113+
it('should only check for existence of properties and ignore their values', () => {
114+
const a = { a: 123, b: 2, c: { d: 1 } }
115+
const b = { a: 1, b: 2, c: { d: 1 } }
116+
const schema = { exists: ['a'] }
117+
assert.deepEqual(
118+
preprocess(a, schema),
119+
preprocess(b, schema)
120+
)
121+
})
122+
123+
it('should only check for existence of properties and ignore their values #2', () => {
124+
const a = { a: null, b: 2, c: { d: 1 } }
125+
const b = { a: 1, b: 2, c: { d: 1 } }
126+
const schema = { exists: ['a'] }
127+
assert.deepEqual(
128+
preprocess(a, schema),
129+
preprocess(b, schema)
130+
)
131+
})
132+
133+
it('should return false when only one object is missing the property', () => {
134+
const a = { b: 2, c: { d: 1 } }
135+
const b = { a: 1, b: 2, c: { d: 1 } }
136+
const schema = { exists: ['a'] }
137+
assert.notDeepEqual(
138+
preprocess(a, schema),
139+
preprocess(b, schema)
140+
)
141+
})
142+
143+
it('should work for multiple properties and nested props', () => {
144+
const a ={ a: 1, b: 2, c: { d: 1 } }
145+
const b ={ a: 1, b: 234, c: { d: 123 } }
146+
const schema ={ exists: ['b', 'c.d'] }
147+
assert.deepEqual(
148+
preprocess(a, schema),
149+
preprocess(b, schema)
150+
)
151+
})
152+
153+
it('should work deeply nested objects', () => {
154+
const a = { a: 1, b: 2, c: { d: 2 } }
155+
const b = { a: 1, b: 2, c: { d: { e: { f: {} } } } }
156+
const schema = { exists: ['c'] }
157+
assert.deepEqual(
158+
preprocess(a, schema),
159+
preprocess(b, schema)
160+
)
161+
})
162+
163+
}) // describe exists
164+
}) // describe preprocess

0 commit comments

Comments
 (0)