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

mozilla / fx-private-relay / 8b39734c-31ce-4dbf-9b05-171797c18cae

02 Oct 2025 04:03PM UTC coverage: 88.815% (-0.05%) from 88.863%
8b39734c-31ce-4dbf-9b05-171797c18cae

Pull #5924

circleci

vpremamozilla
MPP-4348: propagate UTM params from landing page to SubPlat and Accounts URLs
Pull Request #5924: MPP-4348: propagate UTM params from landing page to SubPlat and Accounts URLs

2932 of 3953 branches covered (74.17%)

Branch coverage included in aggregate %.

13 of 21 new or added lines in 5 files covered. (61.9%)

4 existing lines in 3 files now uncovered.

18086 of 19712 relevant lines covered (91.75%)

11.52 hits per line

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

0.0
/frontend/src/pages/flags.page.tsx
1
import { FormEventHandler, useState } from "react";
×
2
import { NextPage } from "next";
3
import { SWRResponse } from "swr";
4
import styles from "./flags.module.scss";
×
5
import { Layout } from "../components/layout/Layout";
×
6
import { Button } from "../components/Button";
×
7
import { getRuntimeConfig } from "../config";
×
8
import { useRuntimeData } from "../hooks/api/runtimeData";
×
9
import { isFlagActive } from "../functions/waffle";
×
10
import { apiFetch, useApiV1 } from "../hooks/api/api";
×
11
import { BlockIcon, CheckIcon } from "../components/Icons";
×
12
import { toast } from "react-toastify";
×
13

14
const Flags: NextPage = () => {
×
15
  const runtimeData = useRuntimeData();
×
16
  const flagData = useFlagData();
×
17
  const [actionInput, setActionInput] = useState("");
×
18
  const [flagInput, setFlagInput] = useState("");
×
19

20
  if (!runtimeData.data) {
×
21
    return null;
×
22
  }
23

24
  if (!isFlagActive(runtimeData.data, "manage_flags") || flagData.error) {
×
25
    const authParams =
NEW
26
      typeof window !== "undefined"
×
27
        ? encodeURIComponent(window.location.search.replace(/^\?/, ""))
28
        : "";
NEW
29
    document.location.assign(
×
30
      `${getRuntimeConfig().fxaLoginUrl}&auth_params=${authParams}`,
31
    );
UNCOV
32
    return null;
×
33
  }
34

35
  const flags =
36
    flagData.data?.filter((flag) => flag.name !== "manage_flags") ?? [];
×
37

38
  async function createOrUpdate(flagName: string, enable: boolean) {
39
    const existingFlag = flagData.data?.find((flag) => flag.name === flagName);
×
40

41
    if (existingFlag) {
×
42
      return apiFetch(`/flags/${existingFlag.id}/`, {
×
43
        method: "PATCH",
44
        body: JSON.stringify({
45
          everyone: enable,
46
        }),
47
      });
48
    }
49

50
    return await apiFetch("/flags/", {
×
51
      method: "POST",
52
      body: JSON.stringify({
53
        name: flagName,
54
        everyone: enable,
55
      }),
56
    });
57
  }
58

59
  const onSubmit: FormEventHandler = async (event) => {
×
60
    event.preventDefault();
×
61

62
    if (!["enable", "disable"].includes(actionInput)) {
×
63
      return;
×
64
    }
65

66
    const enable = actionInput === "enable";
×
67
    const existingFlag = flags.find((flag) => flag.name === flagInput);
×
68

69
    if (existingFlag?.everyone === null) {
×
70
      toast(
×
71
        <>
72
          The flag <output>{flagInput}</output> already has a non-global value,
73
          and therefore cannot be {enable ? "enabled" : "disabled"} for
×
74
          everyone.
75
        </>,
76
        { type: "error" },
77
      );
78
      return;
×
79
    }
80

81
    const response = await createOrUpdate(flagInput, enable);
×
82
    flagData.mutate();
×
83
    if (response.ok) {
×
84
      toast(
×
85
        <>
86
          Flag <output>{flagInput}</output> {enable ? "enabled" : "disabled"}{" "}
×
87
          successfully
88
        </>,
89
        { type: "success" },
90
      );
91
      // Don't make it too easy to update multiple flags in a row
92
      // (to avoid making mistakes):
93
      setActionInput("");
×
94
      setFlagInput("");
×
95
    } else {
96
      toast(
×
97
        <>
98
          Something went wrong {enable ? "enabling" : "disabling"} flag{" "}
×
99
          <output>{flagInput}</output>
100
        </>,
101
        { type: "error" },
102
      );
103
    }
104
    return;
×
105
  };
106

107
  return (
108
    <Layout runtimeData={runtimeData.data}>
109
      <main className={styles.wrapper}>
110
        <table className={styles["flag-list"]}>
111
          <thead>
112
            <tr>
113
              <th>Active?</th>
114
              <th>Flag</th>
115
            </tr>
116
          </thead>
117
          <tbody>
118
            {flags.map((flag) => (
119
              <tr
×
120
                key={flag.name}
121
                className={
122
                  flag.everyone === null
×
123
                    ? styles["is-non-global"]
124
                    : flag.everyone
×
125
                      ? styles["is-active"]
126
                      : styles["is-inactive"]
127
                }
128
              >
129
                <td>
130
                  {flag.everyone ? (
131
                    <CheckIcon alt="Active" />
×
132
                  ) : (
133
                    <BlockIcon alt="Inactive" />
134
                  )}
135
                </td>
136
                <td>{flag.name}</td>
137
              </tr>
138
            ))}
139
          </tbody>
140
        </table>
141
        <form onSubmit={onSubmit} className={styles["flag-form"]}>
142
          <p>
143
            To avoid accidentally enabling/disabling the wrong flags, you have
144
            to type out explicitly what action to take.
145
          </p>
146
          <div className={styles.field}>
147
            <label htmlFor="action">Which flag do you want to modify?</label>
148
            <input
149
              type="text"
150
              id="action"
151
              autoComplete="off"
152
              pattern="(?!manage_flags)(.*)"
153
              required={true}
154
              placeholder={`e.g. \`${flags[0]?.name}\``}
155
              value={flagInput}
156
              onChange={(e) => setFlagInput(e.target.value)}
×
157
            />
158
          </div>
159
          <div className={styles.field}>
160
            <label htmlFor="action">
161
              Do you want to <output>enable</output> or <output>disable</output>{" "}
162
              it?
163
            </label>
164
            <input
165
              type="text"
166
              id="action"
167
              autoComplete="off"
168
              pattern="enable|disable"
169
              required={true}
170
              placeholder="`enable` or `disable`"
171
              value={actionInput}
172
              onChange={(e) => setActionInput(e.target.value)}
×
173
            />
174
          </div>
175
          <Button type="submit">Set flag status</Button>
176
        </form>
177
      </main>
178
    </Layout>
179
  );
180
};
181

182
type Flag = { id: number; name: string; everyone: boolean; note: string };
183
function useFlagData() {
184
  const flagData: SWRResponse<Flag[], unknown> = useApiV1("/flags/");
×
185
  return flagData;
×
186
}
187

188
export default Flags;
×
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