summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
blob: a8ff7cc8fbb06812a911d5d887adb96a5ad78369 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/publicdomain/zero/1.0/
"use strict";

// Tests various aspects of the cert delete confirmation dialog.
// Among other things, tests that for each type of cert that can be deleted:
// 1. The various lines of explanation text are correctly set.
// 2. The implementation correctly falls back through multiple cert attributes
//    to determine what to display to represent a cert.

/**
 * An array of tree items corresponding to TEST_CASES.
 *
 * @type {nsICertTreeItem[]}
 */
var gCertArray = [];

const FAKE_HOST_PORT = "Fake host and port";

/**
 * @typedef TestCase
 * @type {object}
 * @property {string} certFilename
 *           Filename of the cert, or null if we don't want to import a cert for
 *           this test case (i.e. we expect the hostPort attribute of
 *           nsICertTreeItem to be used).
 * @property {string} expectedDisplayString
 *           The string we expect the UI to display to represent the given cert.
 * @property {string} expectedSerialNumber
 *           The serial number we expect the UI to display if it exists.
 */

/**
 * A list of test cases representing certs that get "deleted".
 *
 * @type {TestCase[]}
 */
const TEST_CASES = [
  {
    certFilename: null,
    expectedDisplayString: FAKE_HOST_PORT,
    expectedSerialNumber: null,
  },
  {
    certFilename: "has-cn.pem",
    expectedDisplayString: "Foo",
    expectedSerialNumber: null,
  },
  {
    certFilename: "has-ou.pem",
    expectedDisplayString: "Bar",
    expectedSerialNumber: null,
  },
  {
    certFilename: "has-o.pem",
    expectedDisplayString: "Baz",
    expectedSerialNumber: null,
  },
  {
    certFilename: "has-non-empty-subject.pem",
    expectedDisplayString: "C=US",
    expectedSerialNumber: null,
  },
  {
    certFilename: "has-empty-subject.pem",
    expectedDisplayString: "Certificate with serial number: 0A",
    expectedSerialNumber: "0A",
  },
];

/**
 * Opens the cert delete confirmation dialog.
 *
 * @param {string} tabID
 *        The ID of the cert category tab the certs to delete belong to.
 * @returns {Promise}
 *          A promise that resolves when the dialog has finished loading, with
 *          an array consisting of:
 *            1. The window of the opened dialog.
 *            2. The return value object passed to the dialog.
 */
function openDeleteCertConfirmDialog(tabID) {
  let retVals = {
    deleteConfirmed: false,
  };
  let win = window.openDialog(
    "chrome://pippki/content/deletecert.xhtml",
    "",
    "",
    tabID,
    gCertArray,
    retVals
  );
  return new Promise((resolve, reject) => {
    win.addEventListener(
      "load",
      function () {
        executeSoon(() => resolve([win, retVals]));
      },
      { once: true }
    );
  });
}

add_setup(async function () {
  for (let testCase of TEST_CASES) {
    let cert = null;
    if (testCase.certFilename) {
      cert = await readCertificate(testCase.certFilename, ",,");
    }
    let certTreeItem = {
      hostPort: FAKE_HOST_PORT,
      cert,
      QueryInterface: ChromeUtils.generateQI(["nsICertTreeItem"]),
    };
    gCertArray.push(certTreeItem);
  }
});

/**
 * Test helper for the below test cases.
 *
 * @param {string} tabID
 *        ID of the cert category tab the certs to delete belong to.
 * @param {string} expectedTitleL10nId
 *        The L10nId of title the dialog is expected to have.
 * @param {string} expectedConfirmL10nId
 *        The l10n id of confirmation message the dialog expected to show.
 * @param {string} expectedImpactL10nId
 *        The l10n id of impact the dialog expected to show.
 */
async function testHelper(
  tabID,
  expectedTitleL10nId,
  expectedConfirmL10nId,
  expectedImpactL10nId
) {
  let [win] = await openDeleteCertConfirmDialog(tabID);
  let certList = win.document.getElementById("certlist");

  Assert.deepEqual(
    win.document.l10n.getAttributes(win.document.documentElement),
    expectedTitleL10nId,
    `Actual and expected titles should match for ${tabID}`
  );
  let confirm = win.document.getElementById("confirm");
  Assert.deepEqual(
    win.document.l10n.getAttributes(confirm),
    expectedConfirmL10nId,
    `Actual and expected confirm message should match for ${tabID}`
  );
  let impact = win.document.getElementById("impact");
  Assert.deepEqual(
    win.document.l10n.getAttributes(impact),
    expectedImpactL10nId,
    `Actual and expected impact should match for ${tabID}`
  );

  Assert.equal(
    certList.itemCount,
    TEST_CASES.length,
    `No. of certs displayed should match for ${tabID}`
  );
  for (let i = 0; i < certList.itemCount; i++) {
    let item = certList.getItemAtIndex(i);
    if (TEST_CASES[i].expectedSerialNumber == null) {
      Assert.equal(
        item.label,
        TEST_CASES[i].expectedDisplayString,
        "Actual and expected display string should match for " +
          `index ${i} for ${tabID}`
      );
    } else {
      Assert.deepEqual(
        win.document.l10n.getAttributes(item.children[0]),
        {
          id: "cert-with-serial",
          args: { serialNumber: TEST_CASES[i].expectedSerialNumber },
        },
        "Actual and expected display string should match for " +
          `index ${i} for ${tabID}`
      );
    }
  }

  await BrowserTestUtils.closeWindow(win);
}

// Test deleting certs from the "Your Certificates" tab.
add_task(async function testDeletePersonalCerts() {
  const expectedTitleL10nId = { id: "delete-user-cert-title", args: null };
  const expectedConfirmL10nId = { id: "delete-user-cert-confirm", args: null };
  const expectedImpactL10nId = { id: "delete-user-cert-impact", args: null };
  await testHelper(
    "mine_tab",
    expectedTitleL10nId,
    expectedConfirmL10nId,
    expectedImpactL10nId
  );
});

// Test deleting certs from the "People" tab.
add_task(async function testDeleteOtherPeopleCerts() {
  const expectedTitleL10nId = { id: "delete-email-cert-title", args: null };
  // ’ doesn't seem to work when embedded in the following literals, which is
  // why escape codes are used instead.
  const expectedConfirmL10nId = { id: "delete-email-cert-confirm", args: null };
  const expectedImpactL10nId = { id: "delete-email-cert-impact", args: null };
  await testHelper(
    "others_tab",
    expectedTitleL10nId,
    expectedConfirmL10nId,
    expectedImpactL10nId
  );
});

// Test deleting certs from the "Authorities" tab.
add_task(async function testDeleteCACerts() {
  const expectedTitleL10nId = { id: "delete-ca-cert-title", args: null };
  const expectedConfirmL10nId = { id: "delete-ca-cert-confirm", args: null };
  const expectedImpactL10nId = { id: "delete-ca-cert-impact", args: null };
  await testHelper(
    "ca_tab",
    expectedTitleL10nId,
    expectedConfirmL10nId,
    expectedImpactL10nId
  );
});

// Test that the right values are returned when the dialog is accepted.
add_task(async function testAcceptDialogReturnValues() {
  let [win, retVals] = await openDeleteCertConfirmDialog(
    "ca_tab" /* arbitrary */
  );
  info("Accepting dialog");
  win.document.getElementById("deleteCertificate").acceptDialog();
  await BrowserTestUtils.windowClosed(win);

  Assert.ok(
    retVals.deleteConfirmed,
    "Return value should signal user accepted"
  );
});

// Test that the right values are returned when the dialog is canceled.
add_task(async function testCancelDialogReturnValues() {
  let [win, retVals] = await openDeleteCertConfirmDialog(
    "ca_tab" /* arbitrary */
  );
  info("Canceling dialog");
  win.document.getElementById("deleteCertificate").cancelDialog();
  await BrowserTestUtils.windowClosed(win);

  Assert.ok(
    !retVals.deleteConfirmed,
    "Return value should signal user did not accept"
  );
});