@hubspot/ui-extensions-dev-server 0.0.1-prealpha.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/config.js +11 -10
- package/constants.js +5 -0
- package/dev.js +14 -95
- package/package.json +6 -4
- package/plugins/manifestPlugin.js +8 -3
- package/server.js +148 -0
- package/tests/runTests.js +2 -1
- package/tests/testBuild.js +8 -6
- package/tests/testDevServer.js +58 -15
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# UI Extensions – Dev Server
|
|
2
|
+
|
|
3
|
+
Development server to run and test your HubSpot UI Extensions.
|
|
2
4
|
|
|
3
5
|
## Overview
|
|
4
6
|
This package contains the cli for running HubSpot UI extensions locally, as well as running a production build for debugging purposes.
|
package/config.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
const prompts = require('prompts');
|
|
2
2
|
const logger = require('./logger');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const { MAIN_APP_CONFIG
|
|
4
|
+
const { MAIN_APP_CONFIG } = require('./constants');
|
|
5
5
|
const { getUrlSafeFileName } = require('./utils');
|
|
6
6
|
|
|
7
7
|
async function getExtensionConfig(configuration, extension) {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const extensionOptions = Object.keys(configuration);
|
|
9
|
+
|
|
10
|
+
if (
|
|
11
|
+
(extension && configuration[extension]) ||
|
|
12
|
+
extensionOptions.length === 1
|
|
13
|
+
) {
|
|
14
|
+
const extensionToRun = extension || extensionOptions[0];
|
|
15
|
+
const { data } = configuration[extensionToRun];
|
|
10
16
|
return {
|
|
11
|
-
key:
|
|
17
|
+
key: extensionToRun,
|
|
12
18
|
name: data.title,
|
|
13
19
|
file: data?.module?.file,
|
|
14
20
|
output: data?.output,
|
|
@@ -16,7 +22,6 @@ async function getExtensionConfig(configuration, extension) {
|
|
|
16
22
|
};
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
const extensionOptions = Object.keys(configuration);
|
|
20
25
|
const response = await prompts(
|
|
21
26
|
[
|
|
22
27
|
{
|
|
@@ -65,10 +70,6 @@ function loadConfig() {
|
|
|
65
70
|
// will need to be ran from, the extensions directory
|
|
66
71
|
const configPath = path.join(process.cwd(), `../${MAIN_APP_CONFIG}`);
|
|
67
72
|
|
|
68
|
-
const projectConfig = _loadRequiredConfigFile(
|
|
69
|
-
path.join(process.cwd(), `../../../${PROJECT_CONFIG}`)
|
|
70
|
-
);
|
|
71
|
-
|
|
72
73
|
const mainAppConfig = _loadRequiredConfigFile(configPath);
|
|
73
74
|
|
|
74
75
|
const crmCardsSubConfigFiles = mainAppConfig?.extensions?.crm?.cards;
|
|
@@ -107,7 +108,7 @@ function loadConfig() {
|
|
|
107
108
|
outputConfig[entryPointPath].data.output = getUrlSafeFileName(
|
|
108
109
|
entryPointPath
|
|
109
110
|
);
|
|
110
|
-
outputConfig[entryPointPath].data.appName =
|
|
111
|
+
outputConfig[entryPointPath].data.appName = mainAppConfig.name;
|
|
111
112
|
} catch (e) {
|
|
112
113
|
let errorMessage = e?.message;
|
|
113
114
|
if (e?.code === 'MODULE_NOT_FOUND') {
|
package/constants.js
CHANGED
|
@@ -16,10 +16,15 @@ const ROLLUP_OPTIONS = {
|
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
const EXTENSIONS_MESSAGE_VERSION = 0;
|
|
20
|
+
const WEBSOCKET_MESSAGE_VERSION = 0;
|
|
21
|
+
|
|
19
22
|
module.exports = {
|
|
20
23
|
VITE_DEFAULT_PORT,
|
|
21
24
|
ROLLUP_OPTIONS,
|
|
22
25
|
MAIN_APP_CONFIG,
|
|
23
26
|
PROJECT_CONFIG,
|
|
24
27
|
OUTPUT_DIR,
|
|
28
|
+
EXTENSIONS_MESSAGE_VERSION,
|
|
29
|
+
WEBSOCKET_MESSAGE_VERSION,
|
|
25
30
|
};
|
package/dev.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const chokidar = require('chokidar');
|
|
3
|
-
const { WebSocketServer } = require('ws');
|
|
4
|
-
const http = require('http');
|
|
5
|
-
const logger = require('./logger');
|
|
6
1
|
const { build } = require('vite');
|
|
7
2
|
const { getExtensionConfig } = require('./config');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
3
|
const { ROLLUP_OPTIONS } = require('./constants');
|
|
4
|
+
const startDevServer = require('./server');
|
|
5
|
+
const manifestPlugin = require('./plugins/manifestPlugin');
|
|
11
6
|
|
|
12
|
-
async function
|
|
13
|
-
|
|
14
|
-
build({
|
|
7
|
+
async function _buildDevBundle(outputDir, extensionConfig) {
|
|
8
|
+
await build({
|
|
15
9
|
define: {
|
|
16
10
|
'process.env.NODE_ENV': JSON.stringify(
|
|
17
11
|
process.env.NODE_ENV || 'development'
|
|
@@ -33,99 +27,24 @@ async function _devBuild(config, outputDir, extension) {
|
|
|
33
27
|
formats: ['iife'],
|
|
34
28
|
fileName: () => extensionConfig?.output,
|
|
35
29
|
},
|
|
36
|
-
rollupOptions:
|
|
30
|
+
rollupOptions: {
|
|
31
|
+
...ROLLUP_OPTIONS,
|
|
32
|
+
plugins: [
|
|
33
|
+
...(ROLLUP_OPTIONS.plugins || []),
|
|
34
|
+
manifestPlugin({ minify: false, extensionConfig }),
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
37
|
outDir: outputDir,
|
|
38
38
|
emptyOutDir: true,
|
|
39
39
|
minify: false,
|
|
40
40
|
},
|
|
41
41
|
});
|
|
42
|
-
return extensionConfig;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function _startDevServer(outputDir, port, extensionConfig) {
|
|
46
|
-
const app = express();
|
|
47
|
-
const server = http.createServer(app);
|
|
48
|
-
|
|
49
|
-
// Host the OUTPUT_DIR
|
|
50
|
-
app.use(express.static(outputDir));
|
|
51
|
-
|
|
52
|
-
// Setup websocket server to send messages to browser on bundle update
|
|
53
|
-
const wss = new WebSocketServer({ server });
|
|
54
|
-
|
|
55
|
-
const callback = `http://localhost:${port}/${extensionConfig?.output}`;
|
|
56
|
-
|
|
57
|
-
function broadcast(message) {
|
|
58
|
-
if (wss.clients.size === 0) {
|
|
59
|
-
logger.warn('No browsers connected to update');
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
wss.clients.forEach(client => {
|
|
63
|
-
client.send(JSON.stringify(message));
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
wss.on('connection', client => {
|
|
68
|
-
let base64Callback;
|
|
69
|
-
try {
|
|
70
|
-
base64Callback = fs
|
|
71
|
-
.readFileSync(
|
|
72
|
-
path.join(process.cwd(), outputDir, extensionConfig?.output)
|
|
73
|
-
)
|
|
74
|
-
.toString('base64');
|
|
75
|
-
} catch (e) {
|
|
76
|
-
logger.warn(
|
|
77
|
-
'File not found:',
|
|
78
|
-
path.join(process.cwd(), outputDir, extensionConfig?.output)
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
logger.info('Browser connected and listening for bundle updates');
|
|
83
|
-
client.send(
|
|
84
|
-
JSON.stringify({
|
|
85
|
-
event: 'start',
|
|
86
|
-
appName: extensionConfig?.appName,
|
|
87
|
-
extension: extensionConfig?.name,
|
|
88
|
-
callback: base64Callback
|
|
89
|
-
? `data:text/javascript;base64,${base64Callback}`
|
|
90
|
-
: undefined,
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Start the express and websocket servers
|
|
96
|
-
server.listen({ port }, () => {
|
|
97
|
-
logger.warn(`Listening at ${callback}`);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Setup a watcher on the dist directory and update broadcast
|
|
101
|
-
//to all clients when an event is observed
|
|
102
|
-
chokidar.watch(outputDir).on('change', file => {
|
|
103
|
-
const base64Callback = fs
|
|
104
|
-
.readFileSync(path.join(process.cwd(), file))
|
|
105
|
-
.toString('base64');
|
|
106
|
-
logger.debug(`${file} updated, reloading extension`);
|
|
107
|
-
broadcast({
|
|
108
|
-
event: 'update',
|
|
109
|
-
appName: extensionConfig?.appName,
|
|
110
|
-
extension: extensionConfig?.name,
|
|
111
|
-
callback: `data:text/javascript;base64,${base64Callback}`,
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
process.on('SIGINT', () => {
|
|
116
|
-
logger.warn('\nSending shutdown signal to connected browser');
|
|
117
|
-
broadcast({
|
|
118
|
-
event: 'shutdown',
|
|
119
|
-
appName: extensionConfig?.appName,
|
|
120
|
-
extension: extensionConfig?.name,
|
|
121
|
-
});
|
|
122
|
-
process.exit(0);
|
|
123
|
-
});
|
|
124
42
|
}
|
|
125
43
|
|
|
126
44
|
async function startDevMode(config, outputDir, port, extension) {
|
|
127
|
-
const extensionConfig = await
|
|
128
|
-
|
|
45
|
+
const extensionConfig = await getExtensionConfig(config, extension);
|
|
46
|
+
await _buildDevBundle(outputDir, extensionConfig);
|
|
47
|
+
startDevServer(outputDir, port, extensionConfig);
|
|
129
48
|
}
|
|
130
49
|
|
|
131
50
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/ui-extensions-dev-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"tests/testBuild.js",
|
|
24
24
|
"tests/testDevServer.js",
|
|
25
25
|
"plugins/manifestPlugin.js",
|
|
26
|
-
"index.js"
|
|
26
|
+
"index.js",
|
|
27
|
+
"server.js"
|
|
27
28
|
],
|
|
28
29
|
"license": "MIT",
|
|
29
30
|
"dependencies": {
|
|
@@ -31,11 +32,12 @@
|
|
|
31
32
|
"command-line-args": "^5.2.1",
|
|
32
33
|
"command-line-usage": "^7.0.1",
|
|
33
34
|
"console-log-colors": "^0.4.0",
|
|
35
|
+
"cors": "^2.8.5",
|
|
34
36
|
"express": "^4.18.2",
|
|
35
37
|
"process": "^0.11.10",
|
|
36
38
|
"prompts": "^2.4.2",
|
|
37
39
|
"vite": "^4.0.4",
|
|
38
|
-
"ws": "^8.
|
|
40
|
+
"ws": "^8.13.0"
|
|
39
41
|
},
|
|
40
42
|
"bin": {
|
|
41
43
|
"hs-ui-extensions-dev-server": "run.js"
|
|
@@ -56,5 +58,5 @@
|
|
|
56
58
|
"optional": true
|
|
57
59
|
}
|
|
58
60
|
},
|
|
59
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "abac5960e6277af827714dcd4150ad7606e6a481"
|
|
60
62
|
}
|
|
@@ -12,9 +12,13 @@ function plugin(options = {}) {
|
|
|
12
12
|
name: 'ui-extensions-manifest-generation-plugin',
|
|
13
13
|
enforce: 'post', // run after default rollup plugins
|
|
14
14
|
generateBundle(_rollupOptions, bundle) {
|
|
15
|
-
const {
|
|
15
|
+
const {
|
|
16
|
+
output = DEFAULT_MANIFEST_NAME,
|
|
17
|
+
minify = false,
|
|
18
|
+
extensionConfig,
|
|
19
|
+
} = options;
|
|
16
20
|
try {
|
|
17
|
-
const manifest = _generateManifestContents(bundle);
|
|
21
|
+
const manifest = _generateManifestContents(bundle, extensionConfig);
|
|
18
22
|
this.emitFile({
|
|
19
23
|
type: 'asset',
|
|
20
24
|
source: minify
|
|
@@ -29,9 +33,10 @@ function plugin(options = {}) {
|
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
function _generateManifestContents(bundle) {
|
|
36
|
+
function _generateManifestContents(bundle, extension) {
|
|
33
37
|
const baseManifest = {
|
|
34
38
|
package: _loadPackageFile(),
|
|
39
|
+
extension,
|
|
35
40
|
};
|
|
36
41
|
|
|
37
42
|
// The keys to bundle are the filename without any path information
|
package/server.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const chokidar = require('chokidar');
|
|
3
|
+
const { WebSocketServer } = require('ws');
|
|
4
|
+
const http = require('http');
|
|
5
|
+
const logger = require('./logger');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const cors = require('cors');
|
|
8
|
+
const {
|
|
9
|
+
EXTENSIONS_MESSAGE_VERSION,
|
|
10
|
+
WEBSOCKET_MESSAGE_VERSION,
|
|
11
|
+
} = require('./constants');
|
|
12
|
+
|
|
13
|
+
function startDevServer(outputDir, port, extensionConfig) {
|
|
14
|
+
const callback = `http://hslocal.net:${port}/${extensionConfig?.output}`;
|
|
15
|
+
|
|
16
|
+
const app = express();
|
|
17
|
+
|
|
18
|
+
// Using http.createServer so WebSocket server can be attached before initializing express.
|
|
19
|
+
const server = http.createServer(app);
|
|
20
|
+
|
|
21
|
+
// Setup middleware
|
|
22
|
+
app.use(cors());
|
|
23
|
+
app.use(express.static(outputDir));
|
|
24
|
+
|
|
25
|
+
_addExtensionsEndpoint(app, port);
|
|
26
|
+
|
|
27
|
+
const { broadcast, wss } = _setupWebSocketServer(
|
|
28
|
+
server,
|
|
29
|
+
extensionConfig,
|
|
30
|
+
callback
|
|
31
|
+
);
|
|
32
|
+
_setupChokidarWatcher(broadcast, outputDir);
|
|
33
|
+
|
|
34
|
+
server.listen({ port }, () => {
|
|
35
|
+
logger.warn(`Listening at ${callback}`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
_configureShutDownHandlers(server, wss, broadcast);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function _setupWebSocketServer(server, extensionConfig, callback) {
|
|
42
|
+
const wss = new WebSocketServer({ server });
|
|
43
|
+
|
|
44
|
+
const baseMessage = {
|
|
45
|
+
appName: extensionConfig?.appName,
|
|
46
|
+
title: extensionConfig?.name,
|
|
47
|
+
callback,
|
|
48
|
+
version: WEBSOCKET_MESSAGE_VERSION,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
wss.on('connection', client => {
|
|
52
|
+
logger.info('Browser connected and listening for bundle updates');
|
|
53
|
+
client.send(
|
|
54
|
+
JSON.stringify({
|
|
55
|
+
event: 'start',
|
|
56
|
+
...baseMessage,
|
|
57
|
+
})
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
wss,
|
|
63
|
+
broadcast: message => {
|
|
64
|
+
if (wss.clients.size === 0) {
|
|
65
|
+
logger.warn('No browsers connected to notify');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
wss.clients.forEach(client => {
|
|
69
|
+
client.send(
|
|
70
|
+
JSON.stringify({
|
|
71
|
+
...message,
|
|
72
|
+
...baseMessage,
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Setup a watcher on the dist directory and update broadcast
|
|
81
|
+
//to all clients when an event is observed
|
|
82
|
+
function _setupChokidarWatcher(broadcast, outputDir) {
|
|
83
|
+
chokidar.watch(outputDir).on('all', (event, file) => {
|
|
84
|
+
if (event !== 'change' || event !== 'add' || file === 'manifest.json') {
|
|
85
|
+
// We need to listen to 'change' and 'add' because sometimes chokidar
|
|
86
|
+
// sees it as an 'unlink' and 'add' instead of just a 'change'
|
|
87
|
+
// Since we are adding the manifest.json to the build, we want to ignore changes to that so we don't double send messages
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
logger.info('Bundle updated notifying browser');
|
|
91
|
+
broadcast({
|
|
92
|
+
event: 'update',
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function _addExtensionsEndpoint(server, port) {
|
|
98
|
+
const endpoint = '/extensions';
|
|
99
|
+
server.get(endpoint, (_req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
const manifest = require(path.join(process.cwd(), 'dist/manifest.json'));
|
|
102
|
+
const {
|
|
103
|
+
extension: { appName, name, output },
|
|
104
|
+
} = manifest;
|
|
105
|
+
|
|
106
|
+
const response = {
|
|
107
|
+
websocket: `ws://localhost:${port}`,
|
|
108
|
+
version: EXTENSIONS_MESSAGE_VERSION,
|
|
109
|
+
extensions: [
|
|
110
|
+
{
|
|
111
|
+
appName,
|
|
112
|
+
title: name,
|
|
113
|
+
callback: `http://hslocal.net:${port}/${output}`,
|
|
114
|
+
manifest: {
|
|
115
|
+
...manifest,
|
|
116
|
+
extension: undefined,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
res.status(200).json(response);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
res.status(500).json({
|
|
124
|
+
message: 'Unable to load manifest file',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
logger.warn(`Listening at http://hslocal.net:${port}${endpoint}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function _configureShutDownHandlers(server, wss, broadcast) {
|
|
132
|
+
function shutdown() {
|
|
133
|
+
logger.warn('\nCleaning up after ourselves...');
|
|
134
|
+
// Stop new connections to express server
|
|
135
|
+
broadcast({
|
|
136
|
+
event: 'shutdown',
|
|
137
|
+
});
|
|
138
|
+
wss.close(() => {});
|
|
139
|
+
server.close(() => {});
|
|
140
|
+
logger.warn('Clean up done');
|
|
141
|
+
process.exit(0);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
process.on('SIGINT', shutdown);
|
|
145
|
+
process.on('SIGTERM', shutdown);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
module.exports = startDevServer;
|
package/tests/runTests.js
CHANGED
package/tests/testBuild.js
CHANGED
|
@@ -4,7 +4,9 @@ const path = require('path');
|
|
|
4
4
|
const assert = require('assert');
|
|
5
5
|
|
|
6
6
|
function _testHelper(command, outputDirFiles) {
|
|
7
|
-
|
|
7
|
+
if (command) {
|
|
8
|
+
execSync(command);
|
|
9
|
+
}
|
|
8
10
|
|
|
9
11
|
// Make sure the files are getting generated in the dist dir
|
|
10
12
|
const distDir = path.join(process.cwd(), 'dist');
|
|
@@ -67,11 +69,11 @@ function testBuildWithExtensionFlag(logger) {
|
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
function testDefInfraBuildFileName(logger) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
]);
|
|
72
|
+
const { remoteBuild } = require('../index');
|
|
73
|
+
logger.warn('- Test remoteBuild function 🤞');
|
|
74
|
+
|
|
75
|
+
remoteBuild(process.cwd(), 'ProgressBarApp.tsx', 'dist');
|
|
76
|
+
_testHelper(null, ['ProgressBarApp.js', 'manifest.json']);
|
|
75
77
|
logger.info('- Test build with entrypoint as arg 🚀');
|
|
76
78
|
}
|
|
77
79
|
|
package/tests/testDevServer.js
CHANGED
|
@@ -4,14 +4,20 @@ const path = require('path');
|
|
|
4
4
|
const assert = require('assert');
|
|
5
5
|
const http = require('http');
|
|
6
6
|
const WebSocket = require('ws');
|
|
7
|
+
const {
|
|
8
|
+
WEBSOCKET_MESSAGE_VERSION,
|
|
9
|
+
EXTENSIONS_MESSAGE_VERSION,
|
|
10
|
+
} = require('../constants');
|
|
7
11
|
|
|
8
12
|
const testResults = {
|
|
9
13
|
buildTestPassed: false,
|
|
10
|
-
|
|
14
|
+
expressStaticTestPassed: false,
|
|
15
|
+
extensionsEndpointPassed: false,
|
|
11
16
|
webSocketTestPassed: false,
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
const port = 5172;
|
|
20
|
+
const host = 'hslocal.net';
|
|
15
21
|
|
|
16
22
|
function testDevServer(logger, devServerProcess) {
|
|
17
23
|
logger.warn('\nDev Server Tests started 🤞');
|
|
@@ -32,7 +38,10 @@ function testDevServer(logger, devServerProcess) {
|
|
|
32
38
|
if (data.includes('built in')) {
|
|
33
39
|
testBuild(testResults, logger);
|
|
34
40
|
}
|
|
35
|
-
if (
|
|
41
|
+
if (
|
|
42
|
+
data.includes('listening') &&
|
|
43
|
+
data.includes(`${host}:${port}/extensions`)
|
|
44
|
+
) {
|
|
36
45
|
setTimeout(() => {
|
|
37
46
|
testExpressServer(testResults, logger);
|
|
38
47
|
testWebSocketServer(testResults, logger);
|
|
@@ -71,10 +80,16 @@ function testDevServer(logger, devServerProcess) {
|
|
|
71
80
|
function metConditions() {
|
|
72
81
|
const {
|
|
73
82
|
buildTestPassed,
|
|
74
|
-
|
|
83
|
+
expressStaticTestPassed,
|
|
84
|
+
extensionsEndpointPassed,
|
|
75
85
|
webSocketTestPassed,
|
|
76
86
|
} = testResults;
|
|
77
|
-
return
|
|
87
|
+
return (
|
|
88
|
+
buildTestPassed &&
|
|
89
|
+
expressStaticTestPassed &&
|
|
90
|
+
extensionsEndpointPassed &&
|
|
91
|
+
webSocketTestPassed
|
|
92
|
+
);
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
// Test that the files were built in the proper location and spot
|
|
@@ -83,7 +98,7 @@ function testBuild(results, logger) {
|
|
|
83
98
|
// // Make sure the files are getting generated in the dist dir
|
|
84
99
|
const distDir = path.join(process.cwd(), 'dist');
|
|
85
100
|
const filesInOutputDir = fs.readdirSync(distDir);
|
|
86
|
-
assert.deepStrictEqual(filesInOutputDir, ['PhoneLines.js']);
|
|
101
|
+
assert.deepStrictEqual(filesInOutputDir, ['PhoneLines.js', 'manifest.json']);
|
|
87
102
|
const fileContents = fs
|
|
88
103
|
.readFileSync(path.join(distDir, filesInOutputDir[0]))
|
|
89
104
|
.toString();
|
|
@@ -109,7 +124,7 @@ function testBuild(results, logger) {
|
|
|
109
124
|
function testExpressServer(results, logger) {
|
|
110
125
|
http.get(
|
|
111
126
|
{
|
|
112
|
-
host
|
|
127
|
+
host,
|
|
113
128
|
port,
|
|
114
129
|
path: '/PhoneLines.js',
|
|
115
130
|
},
|
|
@@ -118,7 +133,38 @@ function testExpressServer(results, logger) {
|
|
|
118
133
|
throw Error('Error with express server');
|
|
119
134
|
}
|
|
120
135
|
logger.info('- Express server connected and serving files 🚀');
|
|
121
|
-
results.
|
|
136
|
+
results.expressStaticTestPassed = true;
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
http.get(
|
|
140
|
+
{
|
|
141
|
+
host,
|
|
142
|
+
port,
|
|
143
|
+
path: '/extensions',
|
|
144
|
+
},
|
|
145
|
+
response => {
|
|
146
|
+
let body = '';
|
|
147
|
+
response.on('data', chunk => {
|
|
148
|
+
body += chunk.toString();
|
|
149
|
+
});
|
|
150
|
+
response.on('end', () => {
|
|
151
|
+
assert(response.statusCode === 200);
|
|
152
|
+
body = JSON.parse(body);
|
|
153
|
+
assert(body.extensions);
|
|
154
|
+
assert.equal(body.extensions.length, 1);
|
|
155
|
+
const extension = body.extensions.pop();
|
|
156
|
+
assert(extension.manifest);
|
|
157
|
+
assert.equal(extension.appName, 'Example App React UI');
|
|
158
|
+
assert.equal(extension.title, 'Phone Lines');
|
|
159
|
+
assert.equal(
|
|
160
|
+
extension.callback,
|
|
161
|
+
`http://${host}:${port}/PhoneLines.js`
|
|
162
|
+
);
|
|
163
|
+
assert.equal(body.websocket, `ws://localhost:${port}`);
|
|
164
|
+
assert.strictEquals(body.version, EXTENSIONS_MESSAGE_VERSION);
|
|
165
|
+
logger.info('- Express serving extension data 🚀');
|
|
166
|
+
results.extensionsEndpointPassed = true;
|
|
167
|
+
});
|
|
122
168
|
}
|
|
123
169
|
);
|
|
124
170
|
}
|
|
@@ -126,19 +172,16 @@ function testExpressServer(results, logger) {
|
|
|
126
172
|
// Test the the web socket server is running on the expected port and
|
|
127
173
|
// that we are able to receive messages from it.
|
|
128
174
|
function testWebSocketServer(results, logger) {
|
|
129
|
-
const fileContents = fs
|
|
130
|
-
.readFileSync(path.join('dist', 'PhoneLines.js'))
|
|
131
|
-
.toString('base64');
|
|
132
|
-
|
|
133
175
|
const ws = new WebSocket(`ws://localhost:${port}`);
|
|
134
176
|
ws.on('message', messageBuffer => {
|
|
135
177
|
const message = JSON.parse(messageBuffer.toString());
|
|
136
|
-
assert(message.event === 'start'
|
|
137
|
-
assert(message.appName
|
|
138
|
-
assert.strictEqual(message.
|
|
139
|
-
assert(message.
|
|
178
|
+
assert(message.event === 'start');
|
|
179
|
+
assert.strictEqual(message.appName, 'Example App React UI');
|
|
180
|
+
assert.strictEqual(message.title, 'Phone Lines');
|
|
181
|
+
assert.strictEqual(message.version, WEBSOCKET_MESSAGE_VERSION);
|
|
140
182
|
logger.info('- WebSocket server connected and sending messages 🚀');
|
|
141
183
|
results.webSocketTestPassed = true;
|
|
184
|
+
ws.close(); // The test passed, close the connection
|
|
142
185
|
});
|
|
143
186
|
}
|
|
144
187
|
|