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

mozilla / fx-private-relay / 3bc340e2-329f-4ed2-8700-adaaac8d78c8

15 Dec 2023 06:50PM CUT coverage: 73.514% (-0.1%) from 73.614%
3bc340e2-329f-4ed2-8700-adaaac8d78c8

push

circleci

jwhitlock
Use branch database with production tests

Previously, migrations tests were run with production code, branch
requirements, and branch migrations. Now they run with production
requirements, so that third-party migrations are tested as well.

This uses pytest --reuse-db to create a test database with the branch's
migrations, and then a pip install with the production code. This more
closely emulates the mixed environment during a deploy.

1962 of 2913 branches covered (0.0%)

Branch coverage included in aggregate %.

6273 of 8289 relevant lines covered (75.68%)

19.91 hits per line

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

65.22
/frontend/src/components/dashboard/aliases/MaskCard.tsx
1
import {
2
  createContext,
3
  ReactElement,
4
  ReactNode,
5
  useCallback,
6
  useContext,
7
  useEffect,
8
  useId,
9
  useRef,
10
  useState,
11
} from "react";
2✔
12
import {
13
  RadioGroupProps,
14
  RadioGroupState,
15
  useRadioGroupState,
16
  useToggleState,
17
} from "react-stately";
2✔
18
import {
19
  AriaRadioGroupProps,
20
  AriaRadioProps,
21
  useFocusRing,
22
  useRadio,
23
  useRadioGroup,
24
  useToggleButton,
25
} from "react-aria";
2✔
26
import Link from "next/link";
2✔
27
import Image from "next/image";
2✔
28
import styles from "./MaskCard.module.scss";
2✔
29
import CalendarIcon from "./images/calendar.svg";
2✔
30
import EmailIcon from "./images/email.svg";
2✔
31
import {
32
  AliasData,
33
  isBlockingLevelOneTrackers,
34
} from "../../../hooks/api/aliases";
2✔
35
import { UserData } from "../../../hooks/api/user";
36
import { ProfileData } from "../../../hooks/api/profile";
37
import { RuntimeData } from "../../../hooks/api/runtimeData";
38
import { useL10n } from "../../../hooks/l10n";
2✔
39
import { LabelEditor } from "./LabelEditor";
2✔
40
import { ArrowDownIcon, CopyIcon, LockIcon } from "../../Icons";
2✔
41
import { getLocale } from "../../../functions/getLocale";
2✔
42
import { isFlagActive } from "../../../functions/waffle";
2✔
43
import { renderDate } from "../../../functions/renderDate";
2✔
44
import { AliasDeletionButton } from "./AliasDeletionButton";
2✔
45
import { VisuallyHidden } from "./../../VisuallyHidden";
2✔
46
import HorizontalArrow from "./../images/free-onboarding-horizontal-arrow.svg";
2✔
47
import { AliasDeletionButtonPermanent } from "./AliasDeletionButtonPermanent";
2✔
48

49
export type Props = {
50
  mask: AliasData;
51
  user: UserData;
52
  profile: ProfileData;
53
  onUpdate: (updatedFields: Partial<AliasData>) => void;
54
  onDelete: () => void;
55
  isOpen: boolean;
56
  onChangeOpen: (isOpen: boolean) => void;
57
  showLabelEditor?: boolean;
58
  runtimeData?: RuntimeData;
59
  placeholder?: string;
60
  isOnboarding?: boolean;
61
  children?: ReactNode;
62
  copyAfterMaskGeneration: boolean;
63
};
64

65
export const MaskCard = (props: Props) => {
2✔
66
  const l10n = useL10n();
2✔
67
  const [justCopied, setJustCopied] = useState(false);
2✔
68
  const [promoIsSelected, setPromoIsSelectedState] = useState(false);
2✔
69

70
  const expandButtonRef = useRef<HTMLButtonElement>(null);
2✔
71
  const expandButtonState = useToggleState({
2✔
72
    isSelected: props.isOpen === true,
73
    onChange: props.onChangeOpen,
74
  });
75
  const expandButtonProps = useToggleButton(
2✔
76
    {},
77
    expandButtonState,
78
    expandButtonRef,
79
  ).buttonProps;
80
  // Used to link the expandButton to the area-to-be-expanded:
81
  const detailsElementId = useId();
2✔
82

83
  const copyAddressToClipboard = useCallback(() => {
2✔
84
    navigator.clipboard.writeText(props.mask.full_address);
×
85
    setJustCopied(true);
×
86
    setTimeout(() => {
×
87
      setJustCopied(false);
×
88
    }, 1 * 1000);
89
  }, [props.mask.full_address]);
90

91
  useEffect(() => {
2✔
92
    if (props.copyAfterMaskGeneration) {
2!
93
      copyAddressToClipboard();
×
94
    }
95
  }, [props.copyAfterMaskGeneration, copyAddressToClipboard]);
96

97
  const statNumberFormatter = new Intl.NumberFormat(getLocale(l10n), {
2✔
98
    notation: "compact",
99
    compactDisplay: "short",
100
  });
101

102
  const classNames = [
2✔
103
    styles.card,
104
    props.mask.enabled ? styles["is-enabled"] : styles["is-disabled"],
2!
105
    props.mask.block_list_emails
2!
106
      ? styles["is-blocking-promotionals"]
107
      : styles["is-not-blocking-promotionals"],
108
    isBlockingLevelOneTrackers(props.mask, props.profile)
2!
109
      ? styles["is-removing-trackers"]
110
      : styles["is-not-removing-trackers"],
111
    props.isOnboarding ? styles["is-onboarding"] : "",
2!
112
  ].join(" ");
113

114
  const blockLevel =
115
    props.mask.enabled === false
2!
116
      ? "all"
117
      : props.mask.block_list_emails === true
2!
118
        ? "promotionals"
119
        : "none";
120

121
  return (
122
    <>
123
      <div className={classNames}>
124
        <div className={styles.bar}>
125
          <div className={styles.summary}>
126
            {props.showLabelEditor && (
2✔
127
              <div className={styles["label-editor-wrapper"]}>
128
                <LabelEditor
129
                  label={props.mask.description}
130
                  placeholder={props.placeholder}
131
                  onSubmit={(newLabel) =>
132
                    props.onUpdate({ description: newLabel })
×
133
                  }
134
                />
135
              </div>
136
            )}
137
            <div className={styles["copy-button-wrapper"]}>
138
              <button
139
                className={styles["copy-button"]}
140
                title={l10n.getString("profile-label-click-to-copy")}
141
                aria-label={l10n.getString("profile-label-click-to-copy-alt", {
142
                  address: props.mask.full_address,
143
                })}
144
                onClick={copyAddressToClipboard}
145
              >
146
                <samp className={styles.address}>
147
                  {props.mask.full_address}
148
                </samp>
149
                <span className={styles["copy-icon"]}>
150
                  <CopyIcon alt="" />
151
                </span>
152
              </button>
153
              <span
154
                aria-hidden={!justCopied}
155
                className={styles["copied-confirmation"]}
156
              >
157
                {l10n.getString("profile-label-copied")}
158
              </span>
159
            </div>
160
            <div className={styles["block-level-label"]}>
161
              {props.mask.enabled === false
2!
162
                ? l10n.getString("profile-promo-email-blocking-label-none-2")
163
                : props.mask.block_list_emails === true
2!
164
                  ? l10n.getString(
165
                      "profile-promo-email-blocking-label-promotionals-2",
166
                    )
167
                  : l10n.getString(
168
                      "profile-promo-email-blocking-label-forwarding-2",
169
                    )}
170
            </div>
171
          </div>
172
          <button
173
            {...expandButtonProps}
174
            ref={expandButtonRef}
175
            className={styles["expand-button"]}
176
            aria-expanded={expandButtonState.isSelected}
177
            aria-controls={detailsElementId}
178
          >
179
            <ArrowDownIcon
180
              alt={l10n.getString(
181
                expandButtonState.isSelected
2!
182
                  ? "profile-details-collapse"
183
                  : "profile-details-expand",
184
              )}
185
              width={16}
186
              height={16}
187
            />
188
          </button>
189
        </div>
190
        <div
191
          id={detailsElementId}
192
          className={styles["details-wrapper"]}
193
          hidden={!expandButtonState.isSelected}
194
        >
195
          <div className={styles.details}>
196
            <dl className={styles.stats}>
197
              <div className={`${styles.stat} ${styles["blocked-stat"]}`}>
198
                <dt>{l10n.getString("profile-label-blocked")}</dt>
199
                <dd>{statNumberFormatter.format(props.mask.num_blocked)}</dd>
200
              </div>
201
              <div className={`${styles.stat} ${styles["forwarded-stat"]}`}>
202
                <dt>{l10n.getString("profile-label-forwarded")}</dt>
203
                <dd>{statNumberFormatter.format(props.mask.num_forwarded)}</dd>
204
              </div>
205
              {/* If user is not premium, hide the replies count */}
206
              {props.profile.has_premium && (
2✔
207
                <div className={`${styles.stat} ${styles["replies-stat"]}`}>
208
                  <dt>{l10n.getString("profile-label-replies")}</dt>
209
                  <dd>{statNumberFormatter.format(props.mask.num_replied)}</dd>
210
                </div>
211
              )}
212

213
              {/*
214
              If the back-end does not yet support providing tracker blocking stats,
215
              hide the blocked trackers count:
216
            */}
217
              {isFlagActive(props.runtimeData, "tracker_removal") &&
2!
218
                typeof props.mask.num_level_one_trackers_blocked ===
219
                  "number" && (
220
                  <div
221
                    className={`${styles.stat} ${styles["trackers-removed-stat"]}`}
222
                  >
223
                    <dt>{l10n.getString("profile-label-trackers-removed")}</dt>
224
                    <dd>
225
                      {statNumberFormatter.format(
226
                        props.mask.num_level_one_trackers_blocked,
227
                      )}
228
                    </dd>
229
                  </div>
230
                )}
231
            </dl>
232
            <div
233
              className={`${styles["block-level"]} ${
234
                styles[`is-blocking-${blockLevel}`]
235
              }`}
236
            >
237
              {props.isOnboarding && props.isOpen && (
2!
238
                <div className={styles["onboarding-alias-container"]}>
239
                  <Image src={HorizontalArrow} alt="" />
240
                  <div className={styles["onboarding-alias-text"]}>
241
                    <p>
242
                      {l10n.getString(
243
                        "profile-free-onboarding-copy-mask-what-emails-to-block",
244
                      )}
245
                    </p>
246
                  </div>
247
                </div>
248
              )}
249
              <div className={styles["block-level-setting"]}>
250
                <BlockLevelSegmentedControl
251
                  defaultValue={blockLevel}
252
                  onChange={(blockLevel) => {
253
                    if (blockLevel === "all") {
×
254
                      return props.onUpdate({ enabled: false });
×
255
                    }
256
                    if (blockLevel === "promotionals") {
×
257
                      return props.onUpdate({
×
258
                        enabled: true,
259
                        block_list_emails: true,
260
                      });
261
                    }
262
                    if (blockLevel === "none") {
×
263
                      return props.onUpdate({
×
264
                        enabled: true,
265
                        block_list_emails: false,
266
                      });
267
                    }
268
                  }}
269
                  label={l10n.getString("profile-promo-email-blocking-title")}
270
                >
271
                  <BlockLevelOption
272
                    value="none"
273
                    setPromoSelectedState={setPromoIsSelectedState}
274
                  >
275
                    {l10n.getString("profile-promo-email-blocking-option-none")}
276
                  </BlockLevelOption>
277
                  <BlockLevelOption
278
                    value="promotionals"
279
                    isDisabled={!props.profile.has_premium}
280
                    title={l10n.getString(
281
                      "profile-promo-email-blocking-description-promotionals-locked-label",
282
                    )}
283
                    isPromo={true}
284
                    promoSelectedState={promoIsSelected}
285
                    setPromoSelectedState={setPromoIsSelectedState}
286
                  >
287
                    {!props.profile.has_premium && (
2✔
288
                      <LockIcon
289
                        alt={l10n.getString(
290
                          "profile-promo-email-blocking-description-promotionals-locked-label",
291
                        )}
292
                      />
293
                    )}
294
                    {l10n.getString(
295
                      "profile-promo-email-blocking-option-promotions",
296
                    )}
297
                  </BlockLevelOption>
298
                  <BlockLevelOption
299
                    value="all"
300
                    setPromoSelectedState={setPromoIsSelectedState}
301
                  >
302
                    {l10n.getString("profile-promo-email-blocking-option-all")}
303
                  </BlockLevelOption>
304
                </BlockLevelSegmentedControl>
305
                {promoIsSelected && !props.profile.has_premium ? (
4!
306
                  <div
307
                    className={styles["promotions-locked-description-wrapper"]}
308
                  >
309
                    <strong>
310
                      <LockIcon
311
                        alt={l10n.getString(
312
                          "profile-promo-email-blocking-description-promotionals-locked-label",
313
                        )}
314
                      />
315
                      {l10n.getString(
316
                        "profile-promo-email-blocking-description-promotionals-locked-label",
317
                      )}
318
                    </strong>
319
                    <p>
320
                      {l10n.getString(
321
                        "profile-promo-email-blocking-description-promotionals",
322
                      )}
323
                    </p>
324
                    <Link
325
                      href="/premium#pricing"
326
                      className={styles["upgrade-btn"]}
327
                    >
328
                      {l10n.getString("banner-pack-upgrade-cta")}
329
                    </Link>
330
                  </div>
331
                ) : null}
332
              </div>
333
              <div
334
                className={`${styles["block-level-setting-description"]}
335
              ${
336
                // Only add chevron on mobile for premium users
337
                promoIsSelected &&
2!
338
                !props.profile.has_premium &&
339
                styles["without-chevron"]
340
              }`}
341
              >
342
                {blockLevel === "all" &&
2!
343
                  l10n.getString(
344
                    "profile-promo-email-blocking-description-all-2",
345
                  )}
346
                {blockLevel === "promotionals" && (
2✔
347
                  <>
348
                    <p>
349
                      {l10n.getString(
350
                        "profile-promo-email-blocking-description-promotionals",
351
                      )}
352
                    </p>
353
                    <p>
354
                      <Link href="/faq#faq-promotional-email-blocking">
355
                        {l10n.getString(
356
                          "banner-label-data-notification-body-cta",
357
                        )}
358
                      </Link>
359
                    </p>
360
                  </>
361
                )}
362
                {blockLevel === "none" &&
4✔
363
                  l10n.getString(
364
                    "profile-promo-email-blocking-description-none-3",
365
                  )}
366
              </div>
367
            </div>
368
            <div className={styles.meta}>
369
              <dl>
370
                <div className={styles.metadata}>
371
                  <dt>{l10n.getString("profile-label-created")}</dt>
372
                  <dd>
373
                    <Image src={CalendarIcon} alt="" />
374
                    {renderDate(props.mask.created_at, l10n)}
375
                  </dd>
376
                </div>
377
                <div className={styles.metadata}>
378
                  <dt>{l10n.getString("profile-label-forward-emails")}</dt>
379
                  <dd>
380
                    <Image src={EmailIcon} alt="" />
381
                    {props.user.email}
382
                  </dd>
383
                </div>
384
              </dl>
385
              {!props.isOnboarding && (
2✔
386
                <div className={styles["deletion-button-wrapper"]}>
387
                  {isFlagActive(
388
                    props.runtimeData,
389
                    "custom_domain_management_redesign",
390
                  ) ? (
391
                    <AliasDeletionButtonPermanent
×
392
                      onDelete={props.onDelete}
393
                      alias={props.mask}
394
                    />
395
                  ) : (
396
                    <AliasDeletionButton
397
                      onDelete={props.onDelete}
398
                      alias={props.mask}
399
                    />
400
                  )}
401
                </div>
402
              )}
403
            </div>
404
          </div>
405
        </div>
406
      </div>
407
      {props.isOnboarding && (
2✔
408
        <div
409
          className={
410
            styles[
411
              expandButtonState.isSelected
×
412
                ? "onboarding-open"
413
                : "onboarding-closed"
414
            ]
415
          }
416
        >
417
          {props.children}
418
        </div>
419
      )}
420
    </>
421
  );
422
};
423

424
const BlockLevelContext = createContext<RadioGroupState | null>(null);
2✔
425

426
/**
427
 * A "segmented control" (aka a switch with more than two options) used to set
428
 * the block level.
429
 */
430
const BlockLevelSegmentedControl = (
2✔
431
  props: { children: ReactElement[] } & RadioGroupProps &
432
    AriaRadioGroupProps &
433
    Required<Pick<AriaRadioGroupProps, "label">>,
434
) => {
435
  const state = useRadioGroupState(props);
6✔
436
  const { radioGroupProps, labelProps } = useRadioGroup(
6✔
437
    { orientation: "horizontal", ...props },
438
    state,
439
  );
440

441
  // When the block level state changes externally (i.e. from the custom mask generation success modal), we need to update the block level state within the UI.
442
  useEffect(() => {
6✔
443
    if (props.defaultValue) {
2✔
444
      state.setSelectedValue(props.defaultValue);
2✔
445
    }
446
    // We only want the state to change when the block level default value changes from an update within a mask generation success modal.
447
    // The state itsself is for visually displaying the block level, and should not be added to the dependencies.
448
    // eslint-disable-next-line react-hooks/exhaustive-deps
449
  }, [props.defaultValue]);
450

451
  return (
452
    <div {...radioGroupProps} className={styles["block-level-control-wrapper"]}>
453
      <span {...labelProps} className={styles["block-level-control-label"]}>
454
        {props.label}
455
      </span>
456
      <div className={styles["block-level-segmented-control"]}>
457
        <BlockLevelContext.Provider value={state}>
458
          {props.children}
459
        </BlockLevelContext.Provider>
460
      </div>
461
    </div>
462
  );
463
};
464

465
const BlockLevelOption = (
2✔
466
  props: AriaRadioProps & {
467
    children: ReactNode;
468
    title?: string;
469
    isPromo?: boolean;
470
    promoSelectedState?: boolean;
471
    setPromoSelectedState: (promoSelectedState: boolean) => void;
472
  },
473
) => {
474
  const state = useContext(BlockLevelContext);
18✔
475
  const inputRef = useRef<HTMLInputElement>(null);
18✔
476
  // The `!` is safe here as long as <BlockLevelOption> is only used as a child
477
  // of <BlockLevelSwitch>, which sets the state in the context:
478
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
479
  const { inputProps, isSelected } = useRadio(props, state!, inputRef);
18✔
480
  const { isFocusVisible, focusProps } = useFocusRing();
18✔
481

482
  return (
483
    <label
484
      className={`${isSelected ? styles["is-selected"] : ""} ${
18✔
485
        isFocusVisible ? styles["is-focused"] : ""
18!
486
      } ${props.isDisabled ? styles["is-disabled"] : ""} ${
18✔
487
        props.promoSelectedState ? styles["promo-selected"] : ""
18!
488
      }`}
489
      title={props.title}
490
      onClick={() =>
491
        props.isPromo
×
492
          ? props.setPromoSelectedState(!props.promoSelectedState)
493
          : props.setPromoSelectedState(false)
494
      }
495
    >
496
      <VisuallyHidden>
497
        <input {...inputProps} {...focusProps} ref={inputRef} />
498
      </VisuallyHidden>
499
      {props.children}
500
    </label>
501
  );
502
};
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