UNPKG

3.61 kBJavaScriptView Raw
1'use strict';
2
3const log = require('npmlog');
4const tmp = require('tmp');
5const path = require('path');
6const Bluebird = require('bluebird');
7
8const template = require('./utils/strutils').template;
9const ProcessCtl = require('./process-ctl');
10
11// setup graceful cleanup: removes the created directories when an uncaught exception occurs.
12tmp.setGracefulCleanup();
13
14module.exports = class Launcher {
15 constructor(name, settings, config) {
16 this.name = name;
17 this.config = config;
18 this.settings = settings;
19 this.setupDefaultSettings();
20 this.id = settings.id || String(Math.floor(Math.random() * 10000));
21
22 this.processCtl = new ProcessCtl(name, config);
23 }
24
25 setupDefaultSettings() {
26 let settings = this.settings;
27 if (settings.protocol === 'tap' && !('hide_stdout' in settings)) {
28 settings.hide_stdout = true;
29 }
30 }
31
32 isProcess() {
33 return this.settings.protocol !== 'browser';
34 }
35
36 protocol() {
37 return this.settings.protocol || 'process';
38 }
39
40 commandLine() {
41 if (this.settings.command) {
42 return '"' + this.settings.command + '"';
43 } else if (this.settings.exe) {
44 return '"' + this.settings.exe +
45 ' ' + this.getArgs().join(' ') + '"';
46 }
47 }
48
49 start() {
50 return this.launch();
51 }
52
53 launch() {
54 const settings = this.settings;
55 this.setupBrowserTmpDir();
56
57 return Bluebird.resolve().asCallback(() => {
58 if (settings.setup) {
59 return Bluebird.fromCallback(setupCallback => {
60 settings.setup.call(this, this.config, setupCallback);
61 });
62 }
63
64 return Bluebird.resolve();
65 }).then(() => this.doLaunch());
66 }
67
68 doLaunch() {
69 let settings = this.settings;
70 let options = {};
71
72 if (settings.cwd) {
73 options.cwd = settings.cwd;
74 }
75
76 if (settings.exe) {
77 let args = this.getArgs();
78 args = this.template(args);
79
80 return this.processCtl.spawn(settings.exe, args, options);
81 } else if (settings.command) {
82 let cmd = this.template(settings.command);
83 log.info('cmd: ' + cmd);
84
85 return this.processCtl.exec(cmd, options);
86 } else {
87 return Bluebird.reject(new Error('No command or exe/args specified for launcher ' + this.name));
88 }
89 }
90
91 getId() {
92 return this.isProcess() ? -1 : this.id;
93 }
94
95 getUrl() {
96 let baseUrl = this.config.get('url');
97 let testPage = this.settings.test_page;
98 let id = this.getId();
99
100 return baseUrl + id + (testPage ? '/' + testPage : '');
101 }
102
103 getArgs() {
104 let settings = this.settings;
105 let url = this.getUrl();
106 let args = [url];
107 if (settings.args instanceof Array) {
108 args = settings.args.concat(args);
109 } else if (settings.args instanceof Function) {
110 args = settings.args.call(this, this.config, url);
111 }
112 return args;
113 }
114
115 template(thing) {
116 if (Array.isArray(thing)) {
117 return thing.map(this.template, this);
118 } else {
119 let params = {
120 cwd: this.config.cwd(),
121 url: this.getUrl(),
122 baseUrl: this.config.get('url'),
123 port: this.config.get('port'),
124 testPage: this.settings.test_page || '',
125 id: this.getId()
126 };
127 return template(thing, params);
128 }
129 }
130
131 setupBrowserTmpDir() {
132 const userDataDir = this.config.getUserDataDir();
133 const tmpPath = path.join(userDataDir, 'testem-' + this.id);
134
135 this.browserTmpDirectory = tmp.dirSync({
136 template: `${tmpPath}-XXXXXX`,
137 unsafeCleanup: true
138 });
139 }
140
141 browserTmpDir() {
142 if (!this.browserTmpDirectory) {
143 setupBrowserTmpDir();
144 }
145
146 return this.browserTmpDirectory.name;
147 }
148};