1 | var helper = require('./helper')
|
2 | var events = require('./events')
|
3 | var logger = require('./logger')
|
4 |
|
5 | var Result = require('./browser_result')
|
6 |
|
7 |
|
8 | var READY = 1
|
9 |
|
10 |
|
11 | var EXECUTING = 2
|
12 |
|
13 |
|
14 | var READY_DISCONNECTED = 3
|
15 |
|
16 |
|
17 | var EXECUTING_DISCONNECTED = 4
|
18 |
|
19 |
|
20 | var DISCONNECTED = 5
|
21 |
|
22 | var Browser = function (id, fullName, /* capturedBrowsers */ collection, emitter, socket, timer,
|
23 | /* config.browserDisconnectTimeout */ disconnectDelay,
|
24 | /* config.browserNoActivityTimeout */ noActivityTimeout) {
|
25 | var name = helper.browserFullNameToShort(fullName)
|
26 | var log = logger.create(name)
|
27 | var activeSockets = [socket]
|
28 | var activeSocketsIds = function () {
|
29 | return activeSockets.map(function (s) {
|
30 | return s.id
|
31 | }).join(', ')
|
32 | }
|
33 |
|
34 | var self = this
|
35 | var pendingDisconnect
|
36 | var disconnect = function (reason) {
|
37 | self.state = DISCONNECTED
|
38 | self.disconnectsCount++
|
39 | log.warn('Disconnected (%d times)' + (reason || ''), self.disconnectsCount)
|
40 | emitter.emit('browser_error', self, 'Disconnected' + reason)
|
41 | collection.remove(self)
|
42 | }
|
43 |
|
44 | var noActivityTimeoutId
|
45 | var refreshNoActivityTimeout = noActivityTimeout ? function () {
|
46 | clearNoActivityTimeout()
|
47 | noActivityTimeoutId = timer.setTimeout(function () {
|
48 | self.lastResult.totalTimeEnd()
|
49 | self.lastResult.disconnected = true
|
50 | disconnect(', because no message in ' + noActivityTimeout + ' ms.')
|
51 | emitter.emit('browser_complete', self)
|
52 | }, noActivityTimeout)
|
53 | } : function () {}
|
54 |
|
55 | var clearNoActivityTimeout = noActivityTimeout ? function () {
|
56 | if (noActivityTimeoutId) {
|
57 | timer.clearTimeout(noActivityTimeoutId)
|
58 | noActivityTimeoutId = null
|
59 | }
|
60 | } : function () {}
|
61 |
|
62 | this.id = id
|
63 | this.fullName = fullName
|
64 | this.name = name
|
65 | this.state = READY
|
66 | this.lastResult = new Result()
|
67 | this.disconnectsCount = 0
|
68 |
|
69 | this.init = function () {
|
70 | collection.add(this)
|
71 |
|
72 | events.bindAll(this, socket)
|
73 |
|
74 | log.info('Connected on socket %s with id %s', socket.id, id)
|
75 |
|
76 |
|
77 | emitter.emit('browsers_change', collection)
|
78 |
|
79 | emitter.emit('browser_register', this)
|
80 | }
|
81 |
|
82 | this.isReady = function () {
|
83 | return this.state === READY
|
84 | }
|
85 |
|
86 | this.toString = function () {
|
87 | return this.name
|
88 | }
|
89 |
|
90 | this.onKarmaError = function (error) {
|
91 | if (this.isReady()) {
|
92 | return
|
93 | }
|
94 |
|
95 | this.lastResult.error = true
|
96 | emitter.emit('browser_error', this, error)
|
97 |
|
98 | refreshNoActivityTimeout()
|
99 | }
|
100 |
|
101 | this.onInfo = function (info) {
|
102 | if (this.isReady()) {
|
103 | return
|
104 | }
|
105 |
|
106 |
|
107 | if (helper.isDefined(info.dump)) {
|
108 | emitter.emit('browser_log', this, info.dump, 'dump')
|
109 | }
|
110 |
|
111 | if (helper.isDefined(info.log)) {
|
112 | emitter.emit('browser_log', this, info.log, info.type)
|
113 | }
|
114 |
|
115 | if (
|
116 | !helper.isDefined(info.log) &&
|
117 | !helper.isDefined(info.dump)
|
118 | ) {
|
119 | emitter.emit('browser_info', this, info)
|
120 | }
|
121 |
|
122 | refreshNoActivityTimeout()
|
123 | }
|
124 |
|
125 | this.onStart = function (info) {
|
126 | this.lastResult = new Result()
|
127 | this.lastResult.total = info.total
|
128 |
|
129 | if (info.total === null) {
|
130 | log.warn('Adapter did not report total number of specs.')
|
131 | }
|
132 |
|
133 | emitter.emit('browser_start', this, info)
|
134 | refreshNoActivityTimeout()
|
135 | }
|
136 |
|
137 | this.onComplete = function (result) {
|
138 | if (this.isReady()) {
|
139 | return
|
140 | }
|
141 |
|
142 | this.state = READY
|
143 | this.lastResult.totalTimeEnd()
|
144 |
|
145 | if (!this.lastResult.success) {
|
146 | this.lastResult.error = true
|
147 | }
|
148 |
|
149 | emitter.emit('browsers_change', collection)
|
150 | emitter.emit('browser_complete', this, result)
|
151 |
|
152 | clearNoActivityTimeout()
|
153 | }
|
154 |
|
155 | this.onDisconnect = function (_, disconnectedSocket) {
|
156 | activeSockets.splice(activeSockets.indexOf(disconnectedSocket), 1)
|
157 |
|
158 | if (activeSockets.length) {
|
159 | log.debug('Disconnected %s, still have %s', disconnectedSocket.id, activeSocketsIds())
|
160 | return
|
161 | }
|
162 |
|
163 | if (this.state === READY) {
|
164 | disconnect()
|
165 | } else if (this.state === EXECUTING) {
|
166 | log.debug('Disconnected during run, waiting %sms for reconnecting.', disconnectDelay)
|
167 | this.state = EXECUTING_DISCONNECTED
|
168 |
|
169 | pendingDisconnect = timer.setTimeout(function () {
|
170 | self.lastResult.totalTimeEnd()
|
171 | self.lastResult.disconnected = true
|
172 | disconnect()
|
173 | emitter.emit('browser_complete', self)
|
174 | }, disconnectDelay)
|
175 |
|
176 | clearNoActivityTimeout()
|
177 | }
|
178 | }
|
179 |
|
180 | this.reconnect = function (newSocket) {
|
181 | if (this.state === EXECUTING_DISCONNECTED) {
|
182 | this.state = EXECUTING
|
183 | log.debug('Reconnected on %s.', newSocket.id)
|
184 | } else if (this.state === EXECUTING || this.state === READY) {
|
185 | log.debug('New connection %s (already have %s)', newSocket.id, activeSocketsIds())
|
186 | } else if (this.state === DISCONNECTED) {
|
187 | this.state = READY
|
188 | log.info('Connected on socket %s with id %s', newSocket.id, this.id)
|
189 | collection.add(this)
|
190 |
|
191 |
|
192 | emitter.emit('browsers_change', collection)
|
193 |
|
194 | emitter.emit('browser_register', this)
|
195 | }
|
196 |
|
197 | var exists = activeSockets.some(function (s) {
|
198 | return s.id === newSocket.id
|
199 | })
|
200 | if (!exists) {
|
201 | activeSockets.push(newSocket)
|
202 | events.bindAll(this, newSocket)
|
203 | }
|
204 |
|
205 | if (pendingDisconnect) {
|
206 | timer.clearTimeout(pendingDisconnect)
|
207 | }
|
208 |
|
209 | refreshNoActivityTimeout()
|
210 | }
|
211 |
|
212 | this.onResult = function (result) {
|
213 | if (result.length) {
|
214 | return result.forEach(this.onResult, this)
|
215 | }
|
216 |
|
217 |
|
218 | if (this.isReady()) {
|
219 | return
|
220 | }
|
221 |
|
222 | this.lastResult.add(result)
|
223 |
|
224 | emitter.emit('spec_complete', this, result)
|
225 | refreshNoActivityTimeout()
|
226 | }
|
227 |
|
228 | this.serialize = function () {
|
229 | return {
|
230 | id: this.id,
|
231 | name: this.name,
|
232 | isReady: this.state === READY
|
233 | }
|
234 | }
|
235 |
|
236 | this.execute = function (config) {
|
237 | activeSockets.forEach(function (socket) {
|
238 | socket.emit('execute', config)
|
239 | })
|
240 |
|
241 | this.state = EXECUTING
|
242 | refreshNoActivityTimeout()
|
243 | }
|
244 | }
|
245 |
|
246 | Browser.STATE_READY = READY
|
247 | Browser.STATE_EXECUTING = EXECUTING
|
248 | Browser.STATE_READY_DISCONNECTED = READY_DISCONNECTED
|
249 | Browser.STATE_EXECUTING_DISCONNECTED = EXECUTING_DISCONNECTED
|
250 | Browser.STATE_DISCONNECTED = DISCONNECTED
|
251 |
|
252 | module.exports = Browser
|