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