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

Commit 8d34568

Browse files
author
King Long Tse
authored
E2e tests now run against local dev server (#155)
* added local devServer. tests now defaults to use localhost:8000 and loads ajs from test-e2e/static/analytics.js * extracted test / devServer configuration to config.js; devServer downloads analytics.js automatically * gitignore static/analytics.js * included test-e2e for prettier; added install for make test-e2e target * updated make clean target
1 parent 9548983 commit 8d34568

File tree

10 files changed

+419
-13
lines changed

10 files changed

+419
-13
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ build/
88

99
test-e2e/output
1010
test-e2e/staging
11+
test-e2e/static/analytics.js
12+

Makefile

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ build: clean install
5353
# Remove temporary files and build artifacts.
5454
clean:
5555
rm -rf *.log coverage build
56+
rm -f ./test-e2e/static/analytics.js
5657
.PHONY: clean
5758

5859
# Remove temporary files, build artifacts, and vendor dependencies.
@@ -80,12 +81,24 @@ test-browser: build
8081
test: lint test-browser
8182
.PHONY: test
8283

84+
# Commands to start/stop devServer for e2e tests
85+
start_dev_server = (yarn ts-node ./test-e2e/devServer.ts)
86+
stop_dev_server = (pkill SIGTERM ajs-test-e2e-dev-server)
87+
88+
start-dev-server:
89+
$(call start_dev_server)
90+
91+
stop-dev-server:
92+
$(call stop_dev_server) || true
93+
8394
# Run e2e tests
84-
test-e2e:
95+
test-e2e: install stop-dev-server
96+
yarn ts-node ./test-e2e/devServer.ts &
8597
rm -rf ./test-e2e/output
8698
rm -rf ./test-e2e/staging
8799
mkdir ./test-e2e/staging
88-
npx codeceptjs run --steps
100+
yarn wait-on http://localhost:8000 && npx codeceptjs run --steps
101+
$(call stop_dev_server)
89102
.PHONY: test-e2e
90103

91104
.DEFAULT_GOAL = test

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"scripts": {
1414
"test": "make test",
1515
"lint": "eslint \"./{lib,test}/**/*.js\"",
16-
"format": "prettier-eslint --write --list-different \"./{lib,test}/**/*.{js,json,md}\"",
16+
"format": "prettier-eslint --write --list-different \"./{lib,test,test-e2e}/**/*.{ts,js,json,md}\"",
1717
"precommit": "lint-staged",
1818
"np": "np --no-publish",
1919
"cz": "git-cz"
@@ -65,7 +65,9 @@
6565
"@codeceptjs/mock-request": "^0.3.0",
6666
"@segment/analytics.js-integration": "^3.2.1",
6767
"@segment/eslint-config": "^4.0.0",
68+
"@types/express": "^4.17.6",
6869
"@types/node": "^14.0.6",
70+
"@types/node-fetch": "^2.5.7",
6971
"browserify": "13.0.0",
7072
"browserify-istanbul": "^2.0.0",
7173
"codeceptjs": "^2.6.5",
@@ -78,6 +80,7 @@
7880
"eslint-plugin-mocha": "^5.0.0",
7981
"eslint-plugin-react": "^7.9.1",
8082
"eslint-plugin-require-path-exists": "^1.1.8",
83+
"express": "^4.17.1",
8184
"husky": "^0.14.3",
8285
"istanbul": "^0.4.3",
8386
"jquery": "^3.2.1",
@@ -93,6 +96,7 @@
9396
"karma-summary-reporter": "^1.5.0",
9497
"lint-staged": "^7.2.0",
9598
"mocha": "^2.2.5",
99+
"node-fetch": "^2.6.0",
96100
"np": "^3.0.4",
97101
"phantomjs-prebuilt": "^2.1.7",
98102
"prettier-eslint-cli": "^4.7.1",
@@ -102,6 +106,7 @@
102106
"snyk": "^1.83.0",
103107
"ts-node": "^8.10.2",
104108
"typescript": "^3.9.3",
109+
"wait-on": "^5.0.1",
105110
"watchify": "^3.7.0"
106111
},
107112
"lint-staged": {

test-e2e/ajs.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
Feature('AJS Bundle');
2-
32
const assert = require('assert');
4-
const testSite = 'https://www.library-test-site.com';
5-
const testWriteKey = 'TEWEu8XrcMVejk8GOulbEx7rHGyuuijV';
63

74
Scenario(
85
'User id is stored in cookies and local storage',
96
async (I, testID) => {
10-
I.amOnPage(testSite);
11-
I.loadAJS(testWriteKey);
7+
I.loadAJS({ local: true });
128

139
I.startRecording(testID);
1410
I.click('#page-home');

test-e2e/config.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// These config are used by tests and devServer.
2+
3+
// Port that local dev server binds to
4+
const devServerPort = 8000;
5+
6+
// Settings used by the tests when running with test option {local: false}
7+
const remote = {
8+
// Test website loaded by the tests
9+
testSite: 'https://www.library-test-site.com',
10+
// Test write key used when loading analytics.js
11+
testWriteKey: 'WJq9vAlUO5l2255jMg7eEthbkDtq1svu',
12+
// The test website has a "cdnHost" field that tells it where
13+
// to load analytics.js from.
14+
cdn: 'cdn.segment.com'
15+
};
16+
17+
// Settings used by the tests when running with test option {local: true}
18+
const local = {
19+
testSite: `http://localhost:${devServerPort}`,
20+
testWriteKey: 'WJq9vAlUO5l2255jMg7eEthbkDtq1svu',
21+
cdn: `localhost:${devServerPort}`,
22+
// Local dev server will download analytics.js from
23+
// ${cdn}/analytics.js/v1/${testWriteKey}/analytics.js
24+
// and places it in the test-e2e/static directory
25+
originCDN: 'cdn.segment.com',
26+
// Port that local dev server binds to
27+
devServerPort
28+
};
29+
30+
module.exports = {
31+
local,
32+
remote
33+
};

test-e2e/devServer.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
process.title = 'ajs-test-e2e-dev-server'; // set a process title so that we can call pkill to stop the server using the title
2+
3+
import * as express from 'express';
4+
import * as path from 'path';
5+
import * as config from './config';
6+
import fetch from 'node-fetch';
7+
import * as fs from 'fs';
8+
9+
// handle SIGTERM so that the process returns exit code zero
10+
process.on('SIGTERM', () => {
11+
console.info('SIGTERM received. Exiting.');
12+
process.exit();
13+
});
14+
15+
const app = express();
16+
const port = config.local.devServerPort;
17+
18+
// download analytics.js to ./static directory if it is not already there
19+
(async () => {
20+
const ajsPath = path.join(__dirname, 'static', 'analytics.js');
21+
if (fs.existsSync(ajsPath)) {
22+
console.log(`Found analytics.js locally at ${ajsPath}`);
23+
return;
24+
}
25+
console.log(`analytics.js not found. Downloading ajs to ${ajsPath}`);
26+
const resp = await fetch(
27+
`https://${config.local.originCDN}/analytics.js/v1/${
28+
config.local.testWriteKey
29+
}/analytics.js`
30+
);
31+
if (!resp.ok) {
32+
console.log(
33+
`Failed downloading analytics.js: ${resp.status} ${resp.statusText}`
34+
);
35+
process.exit(1);
36+
}
37+
fs.writeFileSync(ajsPath, await resp.text());
38+
if (fs.existsSync(ajsPath)) {
39+
console.log(`Downloaded analytics.js to ${ajsPath}`);
40+
}
41+
})();
42+
43+
// the tests loads tests from localhost:8000/analytics.js/v1/<write-key>/analytics.js
44+
app.use(
45+
'/analytics.js/v1/:writeKey/',
46+
express.static(path.join(__dirname, 'static'))
47+
);
48+
app.use('/', express.static(path.join(__dirname, 'static')));
49+
50+
app.listen(port, () =>
51+
console.log(`test-e2e dev server listening at http://localhost:${port}`)
52+
);

test-e2e/static/index.html

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<html>
2+
<head>
3+
<title>AJS tester</title>
4+
</head>
5+
<body>
6+
<form method="GET">
7+
<input type="text" name="writeKey" placeholder="Write key" />
8+
<input type="text" name="cdnHost" placeholder="cdn.segment.com" />
9+
<button>Load</button>
10+
</form>
11+
<p id='status-msg'></p>
12+
13+
<button id='track-product-viewed' onclick="analytics.track('Product Viewed')">
14+
Track: Product Viewed
15+
</button>
16+
<button id='track-checkout-started' onclick="analytics.track('Checkout Started')">
17+
Track: Checkout Started
18+
</button>
19+
<button id='track-coupon-denied' onclick="analytics.track('Coupon Denied')">
20+
Track: Coupon Denied
21+
</button>
22+
<br />
23+
<button id='page-home' onclick="analytics.page('Home')">Page: Home</button>
24+
<button id='page-about' onclick="analytics.page('About')">Page: About</button>
25+
<button id='page-contact' onclick="analytics.page('Contact')">Page: Contact</button>
26+
<br />
27+
<button id='identify-fathy' onclick="analytics.identify('fathy')">Identify: fathy</button>
28+
<button id='identify-spongebob' onclick="analytics.identify('spongebob')">
29+
Identify: spongebob
30+
</button>
31+
<button
32+
id='group'
33+
onclick="analytics.group(
34+
'group name',
35+
{
36+
'address': {
37+
'city': 'Vancouver',
38+
'country': 'Canada',
39+
'postalCode': 'V6b3E2',
40+
'state': 'BC',
41+
'street': '21 Jump St'
42+
},
43+
'avatar': 'does not exist',
44+
'createdAt': new Date(),
45+
'description': 'a fake group',
46+
'email': '[email protected]',
47+
'employees': 3,
48+
'id': 1,
49+
'industry': 'sw eng',
50+
'name': 'libweb',
51+
'phone': '555-pizza',
52+
'website': 'www.google.com',
53+
'plan': 'business'
54+
},
55+
{
56+
'integrations': {
57+
'All': true
58+
}
59+
}, function() { console.log('group callback triggered')})"
60+
>
61+
Group
62+
</button>
63+
64+
<button
65+
id='alias'
66+
onclick="analytics.alias('userId', 'previous id', {
67+
'integrations': { 'All': true }
68+
}, function(){ console.log('alias callback triggered')})"
69+
>
70+
Alias
71+
</button>
72+
73+
<br />
74+
75+
<script>
76+
const { searchParams } = new URL(document.location);
77+
const writeKey = searchParams.get("writeKey");
78+
const cdnHost = searchParams.get("cdnHost") || 'cdn.segment.com';
79+
const status = (s) => (document.querySelector("p").innerText = s);
80+
81+
document.querySelector("input").value = writeKey;
82+
83+
if (writeKey) {
84+
!(function () {
85+
var analytics = (window.analytics = window.analytics || []);
86+
if (!analytics.initialize)
87+
if (analytics.invoked)
88+
window.console &&
89+
console.error &&
90+
console.error("Segment snippet included twice.");
91+
else {
92+
analytics.invoked = !0;
93+
analytics.methods = [
94+
"trackSubmit",
95+
"trackClick",
96+
"trackLink",
97+
"trackForm",
98+
"pageview",
99+
"identify",
100+
"reset",
101+
"group",
102+
"track",
103+
"ready",
104+
"alias",
105+
"debug",
106+
"page",
107+
"once",
108+
"off",
109+
"on",
110+
];
111+
analytics.factory = function (t) {
112+
return function () {
113+
var e = Array.prototype.slice.call(arguments);
114+
e.unshift(t);
115+
analytics.push(e);
116+
return analytics;
117+
};
118+
};
119+
for (var t = 0; t < analytics.methods.length; t++) {
120+
var e = analytics.methods[t];
121+
analytics[e] = analytics.factory(e);
122+
}
123+
analytics.load = function (t, e) {
124+
var n = document.createElement("script");
125+
n.type = "text/javascript";
126+
n.async = !0;
127+
n.src =
128+
"http://" +
129+
cdnHost +
130+
"/analytics.js/v1/" +
131+
t +
132+
"/analytics.js";
133+
var a = document.getElementsByTagName("script")[0];
134+
a.parentNode.insertBefore(n, a);
135+
analytics._loadOptions = e;
136+
};
137+
analytics.SNIPPET_VERSION = "4.1.0";
138+
analytics.load(writeKey);
139+
analytics.page();
140+
}
141+
})();
142+
143+
status("loading ajs");
144+
145+
analytics.ready(() => {
146+
analytics.reset();
147+
var f = function ({ payload, next, integrations }) {
148+
next(payload);
149+
};
150+
var f2 = function ({ payload, next, integrations }) {
151+
next(payload);
152+
};
153+
analytics.addSourceMiddleware(f);
154+
analytics.addSourceMiddleware(f2);
155+
return status(
156+
`ajs ${analytics.VERSION} loaded, write key: ${analytics._integrations["Segment.io"].options.apiKey}`
157+
);
158+
});
159+
} else {
160+
status("ajs not loaded, enter a write key");
161+
}
162+
</script>
163+
</body>
164+
</html>

test-e2e/steps.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/// <reference types='codeceptjs' />
1+
// / <reference types='codeceptjs' />
22
type steps_file = typeof import('./steps_file');
33

44
declare namespace CodeceptJS {

test-e2e/steps_file.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
const fs = require('fs');
2+
const config = require('./config');
23
// in this file you can append custom step methods to 'I' object
34

45
module.exports = function() {
56
return actor({
6-
loadAJS: async function(testWriteKey) {
7+
loadAJS: async function(options /* : {local: boolean}*/) {
8+
let testSite = config.remote.testSite;
9+
let testWriteKey = config.remote.testWriteKey;
10+
let cdn = config.remote.cdn;
11+
if (options.local) {
12+
testSite = config.local.testSite;
13+
testWriteKey = config.local.testWriteKey;
14+
cdn = config.local.cdn;
15+
}
16+
this.amOnPage(testSite);
17+
this.fillField('cdnHost', cdn);
718
this.fillField('writeKey', testWriteKey);
819
this.click('Load');
920

0 commit comments

Comments
 (0)