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

mozilla / blurts-server / fe6cfeb1-ae03-482b-9144-7da84a045922

pending completion
fe6cfeb1-ae03-482b-9144-7da84a045922

push

circleci

Vincent
All states for breach results

282 of 1655 branches covered (17.04%)

Branch coverage included in aggregate %.

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

959 of 4458 relevant lines covered (21.51%)

3.59 hits per line

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

0.0
/src/client/js/partials/exposure-scan.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
/** @type {HTMLDivElement} */
6
// @ts-ignore: We guard against a null value by not calling init():
7
const exposureScanPartial = document.querySelector("[data-partial='exposureScan']")
×
8

9
if (exposureScanPartial) {
×
10
  init()
×
11
}
12

13
async function init () {
14
  const urlParams = new URLSearchParams(document.location.search)
×
15
  const emailFromUrl = urlParams.get('email')
×
16
  /** @type {HTMLTemplateElement} */
17
  const dataEl = exposureScanPartial.querySelector('#data')
×
18
  dataEl.dataset.email = emailFromUrl
×
19
  const sanitisedEmail = dataEl.dataset.email.trim()
×
20

21
  const res = await fetch('/api/v1/scan/', {
×
22
    headers: {
23
      'Content-Type': 'application/json',
24
      'x-csrf-token': dataEl.dataset.csrfToken,
25
      Accept: 'application/json'
26
    },
27
    mode: 'same-origin',
28
    method: 'POST',
29
    body: JSON.stringify({
30
      email: sanitisedEmail
31
    })
32
  })
33

34
  if (!res.ok) {
×
35
    return
×
36
  }
37

38
  /** @type {import("../../../controllers/request-breach-scan").RequestBreachScanResponse} */
39
  const responseBody = await res.json()
×
40

41
  if (!responseBody.success) {
×
42
    return
×
43
  }
44

45
  if (responseBody.total > 6) {
×
46
    showOverflowingBreachResults(responseBody)
×
47
  } else if (responseBody.total > 0) {
×
48
    showSomeBreachResults(responseBody)
×
49
  } else {
50
    showNoBreachesResult(responseBody)
×
51
  }
52
}
53

54
/**
55
 * @param {import("../../../controllers/request-breach-scan").RequestBreachScanSuccessResponse} response
56
 */
57
function showOverflowingBreachResults (response) {
58
  const heading = document.createElement('h1')
×
59
  heading.innerHTML = response.heading
×
60
  exposureScanPartial.querySelector('#exposure-scan-results-overflow .exposure-scan-hero-content').insertAdjacentElement('afterbegin', heading)
×
61
  const breachCards = response.breaches.map((breach, index) => getBreachCard(breach, response.logos[index], response.dataClassStrings[index]))
×
62
  const breachContainer = exposureScanPartial.querySelector('#exposure-scan-results-overflow .exposure-scan-breaches')
×
63
  breachCards.forEach(card => breachContainer.appendChild(card))
×
64
  exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
65
  exposureScanPartial.querySelector('#exposure-scan-results-overflow').hidden = false
×
66
}
67

68
/**
69
 * @param {import("../../../controllers/request-breach-scan").RequestBreachScanSuccessResponse} response
70
 */
71
function showSomeBreachResults (response) {
72
  const heading = document.createElement('h1')
×
73
  heading.innerHTML = response.heading
×
74
  exposureScanPartial.querySelector('#exposure-scan-results-some .exposure-scan-hero-content').insertAdjacentElement('afterbegin', heading)
×
75
  const breachCards = response.breaches.map((breach, index) => getBreachCard(breach, response.logos[index], response.dataClassStrings[index]))
×
76
  const breachContainer = exposureScanPartial.querySelector('#exposure-scan-results-some .exposure-scan-breaches')
×
77
  breachCards.forEach(card => breachContainer.appendChild(card))
×
78
  exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
79
  exposureScanPartial.querySelector('#exposure-scan-results-some').hidden = false
×
80
}
81

82
/**
83
 * @param {import("../../../controllers/request-breach-scan").RequestBreachScanSuccessResponse} response
84
 */
85
function showNoBreachesResult (response) {
86
  const heading = document.createElement('h1')
×
87
  heading.innerHTML = response.heading
×
88
  exposureScanPartial.querySelector('#exposure-scan-results-none .exposure-scan-hero-content').insertAdjacentElement('afterbegin', heading)
×
89
  exposureScanPartial.querySelector('#exposure-scan-loading').hidden = true
×
90
  exposureScanPartial.querySelector('#exposure-scan-results-none').hidden = false
×
91
}
92

93
function getBreachCard (breach, logo, dataClassStrings) {
94
  const newCard = document.getElementById('breach-template').content.cloneNode(true)
×
95
  newCard.querySelector('.exposure-scan-breach-company-logo').innerHTML = logo
×
96
  newCard.querySelector('.exposure-scan-breach-company-name').textContent = breach.Title
×
97
  newCard.querySelector('.exposure-scan-breach-added dd').textContent = new Date(breach.AddedDate).toLocaleString(navigator.languages, { year: 'numeric', month: 'long', day: 'numeric' })
×
98
  newCard.querySelector('.exposure-scan-breach-data dd').textContent = formatList(dataClassStrings)
×
99

100
  return newCard
×
101
}
102

103
function formatList (list) {
104
  if (typeof Intl.ListFormat === 'undefined') {
×
105
    return list.join(', ')
×
106
  }
107

108
  return (new Intl.ListFormat(navigator.languages, { type: 'unit', style: 'short' })).format(list)
×
109
}
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