summaryrefslogtreecommitdiffstats
path: root/browser/components/attribution/test/xpcshell/test_AttributionCode.js
blob: 1296420cd761842b9dd6bb5acaa519ee81b76c8f (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
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */
"use strict";

const { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);

const { sinon } = ChromeUtils.importESModule(
  "resource://testing-common/Sinon.sys.mjs"
);

add_task(async () => {
  await setupStubs();
});

/**
 * Test validation of attribution codes,
 * to make sure we reject bad ones and accept good ones.
 */
add_task(async function testValidAttrCodes() {
  let currentCode = null;
  for (let entry of validAttrCodes) {
    currentCode = entry.code;
    // Attribution for MSIX builds works quite differently than regular Windows
    // builds: the attribution codes come from a Windows API that we've wrapped
    // with an XPCOM class. We don't have a way to inject codes into the build,
    // so instead we mock out our XPCOM class and return the desired values from
    // there. (A potential alternative is to have MSIX tests rely on attribution
    // files, but that more or less invalidates the tests.)
    if (
      AppConstants.platform === "win" &&
      Services.sysinfo.getProperty("hasWinPackageId")
    ) {
      // In real life, the attribution codes returned from Microsoft APIs
      // are not URI encoded, and the AttributionCode code that deals with
      // them expects that - so we have to simulate that as well.
      sinon
        .stub(AttributionCode, "msixCampaignId")
        .get(() => decodeURIComponent(currentCode));
    } else {
      await AttributionCode.writeAttributionFile(currentCode);
    }
    AttributionCode._clearCache();
    let result = await AttributionCode.getAttrDataAsync();
    Assert.deepEqual(
      result,
      entry.parsed,
      "Parsed code should match expected value, code was: " + currentCode
    );
  }
  AttributionCode._clearCache();
});

/**
 * Make sure codes with various formatting errors are not seen as valid.
 */
add_task(async function testInvalidAttrCodes() {
  let currentCode = null;
  for (let code of invalidAttrCodes) {
    currentCode = code;

    if (
      AppConstants.platform === "win" &&
      Services.sysinfo.getProperty("hasWinPackageId")
    ) {
      if (code.includes("not set")) {
        // One of the "invalid" codes that we test is an unescaped one.
        // This is valid for most platforms, but we actually _expect_
        // unescaped codes for MSIX builds, so that particular test is not
        // valid for this case.
        continue;
      }

      sinon
        .stub(AttributionCode, "msixCampaignId")
        .get(() => decodeURIComponent(currentCode));
    } else {
      await AttributionCode.writeAttributionFile(currentCode);
    }
    AttributionCode._clearCache();
    let result = await AttributionCode.getAttrDataAsync();
    Assert.deepEqual(
      result,
      {},
      "Code should have failed to parse: " + currentCode
    );
  }
  AttributionCode._clearCache();
});

/**
 * Test the cache by deleting the attribution data file
 * and making sure we still get the expected code.
 */
let condition = {
  // MSIX attribution codes are not cached by us, thus this test is
  // unnecessary for those builds.
  skip_if: () =>
    AppConstants.platform === "win" &&
    Services.sysinfo.getProperty("hasWinPackageId"),
};
add_task(condition, async function testDeletedFile() {
  // Set up the test by clearing the cache and writing a valid file.
  await AttributionCode.writeAttributionFile(validAttrCodes[0].code);
  let result = await AttributionCode.getAttrDataAsync();
  Assert.deepEqual(
    result,
    validAttrCodes[0].parsed,
    "The code should be readable directly from the file"
  );

  // Delete the file and make sure we can still read the value back from cache.
  await AttributionCode.deleteFileAsync();
  result = await AttributionCode.getAttrDataAsync();
  Assert.deepEqual(
    result,
    validAttrCodes[0].parsed,
    "The code should be readable from the cache"
  );

  // Clear the cache and check we can't read anything.
  AttributionCode._clearCache();
  result = await AttributionCode.getAttrDataAsync();
  Assert.deepEqual(
    result,
    {},
    "Shouldn't be able to get a code after file is deleted and cache is cleared"
  );
});