• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / 381ce46f-cdf3-44aa-9a79-1b0ef3231d2b

pending completion
381ce46f-cdf3-44aa-9a79-1b0ef3231d2b

push

circleci

Florian Zia
feat: Add client error and handle error classes

282 of 1801 branches covered (15.66%)

Branch coverage included in aggregate %.

79 of 79 new or added lines in 2 files covered. (100.0%)

959 of 4740 relevant lines covered (20.23%)

3.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/shared/error.js
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4

5
// General error strings
6
const badRequestErrorMessage = ''
×
7
const unauthorizedErrorMessage = 'You don’t have permission to access this content.'
×
8
const forbiddenErrorMessage = 'You don’t have permission to access this content.'
×
9
const methodNotAllowedErrorMessage = 'Method Not Allowed'
×
10
const tooManyRequestsErrorMessage = 'Too many requests received. Please try again later.'
×
11
const internalServerErrorMessage = 'Something went wrong. Please try again or come back later.'
×
12

13
// Custom error strings
14
const userInputErrorMessage = 'The information provided is not valid. Please check and try again.'
×
15
const resolveBreachErrorMessage = ''
×
16
const oneRepErrorMessage = ''
×
17
const duplicateEmailErrorMessage = 'This email has already been verified.'
×
18
const addEmailErrorMessage = 'Email couldn’t be added. Please try again.'
×
19
const emailVerificationErrorMessage = 'Email verification not sent. Please try again.'
×
20
const breachesErrorMessage = 'We couldn’t search for the latest breaches. Please refresh or try again later.'
×
21

22
/**
23
 * Server-side errors
24
 */
25

26
class ServerSideError extends Error {
27
  /**
28
   * @param {string?} message
29
   * @param {object} config
30
   */
31
  constructor (message, ...config) {
32
    super(message, ...config)
×
33

34
    // if (!process) {
35
    console.info('This error is only supposed to be used server-side.')
×
36
    // }
37
  }
38
}
39

40
class BadRequestError extends ServerSideError {
41
  /**
42
   * @param {string?} message
43
   * @param {object} config
44
   */
45
  constructor (message = badRequestErrorMessage, ...config) {
×
46
    super(message, ...config)
×
47
    this.statusCode = 400
×
48
    this.name = 'Bad request'
×
49
  }
50
}
51

52
class UnauthorizedError extends ServerSideError {
53
  /**
54
   * @param {string?} message
55
   * @param {object} config
56
   */
57
  constructor (message = unauthorizedErrorMessage, ...config) {
×
58
    super(message, ...config)
×
59
    this.statusCode = 401
×
60
    this.name = 'Unauthorized'
×
61
  }
62
}
63

64
class ForbiddenError extends ServerSideError {
65
  /**
66
   * @param {string?} message
67
   * @param {object} config
68
   */
69
  constructor (message = forbiddenErrorMessage, ...config) {
×
70
    super(message, ...config)
×
71
    this.statusCode = 403
×
72
    this.name = 'Forbidden'
×
73
  }
74
}
75

76
class MethodNotAllowedError extends ServerSideError {
77
  /**
78
   * @param {string?} message
79
   * @param {object} config
80
   */
81
  constructor (message = methodNotAllowedErrorMessage, ...config) {
×
82
    super(message, ...config)
×
83
    this.statusCode = 405
×
84
    this.name = 'Method Not Allowed'
×
85
  }
86
}
87

88
class TooManyRequestsError extends ServerSideError {
89
  /**
90
   * @param {string?} message
91
   * @param {object} config
92
   */
93
  constructor (message = tooManyRequestsErrorMessage, ...config) {
×
94
    super(message, ...config)
×
95
    this.statusCode = 429
×
96
    this.name = 'Too Many Requests'
×
97
  }
98
}
99

100
class InternalServerError extends ServerSideError {
101
  /**
102
   * @param {string?} message
103
   * @param {object} config
104
   */
105
  constructor (message = internalServerErrorMessage, ...config) {
×
106
    super(message, ...config)
×
107
    this.statusCode = 500
×
108
    this.name = 'Internal Server Error'
×
109
  }
110
}
111

112
/**
113
 * Custom errors
114
 */
115

116
class UserInputError extends BadRequestError {
117
  /**
118
   * @param {string?} message
119
   * @param {object} config
120
   */
121
  constructor (message = userInputErrorMessage, ...config) {
×
122
    super(message, ...config)
×
123
    this.name = 'User Input Error'
×
124
  }
125
}
126

127
class ResolveBreachError extends InternalServerError {
128
  /**
129
   * Can’t mark breach as resolved
130
   *
131
   * @param {string?} message
132
   * @param {object} config
133
   */
134
  constructor (message = resolveBreachErrorMessage, ...config) {
×
135
    super(message, ...config)
×
136
    this.name = 'Resolve Breach Error'
×
137
  }
138
}
139

140
class OneRepError extends InternalServerError {
141
  /**
142
   * OneRep scan can’t start
143
   *
144
   * @param {string?} message
145
   * @param {object} config
146
   */
147
  constructor (message = oneRepErrorMessage, ...config) {
×
148
    super(message, ...config)
×
149
    this.name = 'OneRep Error'
×
150
  }
151
}
152

153
class DuplicateEmailError extends ServerSideError {
154
  /**
155
   * Duplicate email added
156
   *
157
   * @param {string?} message
158
   * @param {object} config
159
   */
160
  constructor (message = duplicateEmailErrorMessage, ...config) {
×
161
    super(message, ...config)
×
162
    this.statusCode = 460
×
163
    this.name = 'Duplicate Email Error'
×
164
  }
165
}
166

167
class AddEmailError extends ServerSideError {
168
  /**
169
   * Could not add email
170
   *
171
   * @param {string?} message
172
   * @param {object} config
173
   */
174
  constructor (message = addEmailErrorMessage, ...config) {
×
175
    super(message, ...config)
×
176
    this.statusCode = 461
×
177
    this.name = 'Add Email Error'
×
178
  }
179
}
180

181
class EmailVerificationError extends ServerSideError {
182
  /**
183
   * Email verification can’t be sent
184
   *
185
   * @param {string?} message
186
   * @param {object} config
187
   */
188
  constructor (message = emailVerificationErrorMessage, ...config) {
×
189
    super(message, ...config)
×
190
    this.statusCode = 462
×
191
    this.name = 'Email Verification Error'
×
192
  }
193
}
194

195
class BreachesError extends ServerSideError {
196
  /**
197
   * Data breach page: we couldn’t fetch the latest breaches
198
   *
199
   * @param {string?} message
200
   * @param {object} config
201
   */
202
  constructor (message = breachesErrorMessage, ...config) {
×
203
    super(message, ...config)
×
204
    this.statusCode = 463
×
205
    this.name = 'Breaches Error'
×
206
  }
207
}
208

209
/**
210
 * Client-side errors
211
 */
212

213
class ClientSideError extends Error {
214
  /**
215
   * @param {string?} message
216
   * @param {object} config
217
   */
218
  constructor (message, ...config) {
219
    super(message, ...config)
×
220

221
    if (!window && !window.document) {
×
222
      console.info('This error is only supposed to be used client-side.')
×
223
    }
224
  }
225
}
226

227
const ErrorActionTypes = {
×
228
  None: 'none',
229
  Redirect: 'redirect',
230
  Toast: 'toast'
231
}
232

233
class ClientError extends Error {
234
  /**
235
   * @param {string} message
236
   * @param {object} config
237
   */
238
  constructor (message, ...config) {
239
    super(...config)
×
240

241
    if (!window && !window.document) {
×
242
      console.info('This error is only supposed to be used client-side.')
×
243
    }
244

245
    const [{
246
      action = ErrorActionTypes.None,
×
247
      targetHref = '/'
×
248
    }] = config
×
249

250
    this.config = { action, targetHref }
×
251
    this.message = message
×
252
    this.toast = null
×
253

254
    this.handleConfig()
×
255
  }
256

257
  handleConfig () {
258
    switch (this.config.action) {
×
259
      case ErrorActionTypes.Redirect:
260
        this.handleRedirect()
×
261
        break
×
262
      case ErrorActionTypes.Toast:
263
        this.showToastNotification()
×
264
        break
×
265
      case ErrorActionTypes.None:
266
      default:
267
        // do nothing
268
    }
269
  }
270

271
  handleRedirect () {
272
    if (document.location.pathname !== this.config.targetHref) {
×
273
      document.location.href = this.config.targetHref
×
274
    }
275
  }
276

277
  showToastNotification () {
278
    if (this.toast) {
×
279
      return
×
280
    }
281

282
    this.toast = document.createElement('toast-alert')
×
283
    this.toast.textContent = this.message
×
284
    document.body.append(this.toast)
×
285
  }
286
}
287

288
export {
289
  ServerSideError,
290
  BadRequestError,
291
  UnauthorizedError,
292
  ForbiddenError,
293
  MethodNotAllowedError,
294
  TooManyRequestsError,
295
  InternalServerError,
296
  UserInputError,
297
  ResolveBreachError,
298
  OneRepError,
299
  DuplicateEmailError,
300
  AddEmailError,
301
  EmailVerificationError,
302
  BreachesError,
303
  ClientSideError,
304
  ClientError
305
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc