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

blockcoders / kuma-wallet / ebbd3c69-fda1-4bf1-80ea-77bef87d3d87

pending completion
ebbd3c69-fda1-4bf1-80ea-77bef87d3d87

Pull #8

circleci

Ruben
fix tests
Pull Request #8: Milestone 2

876 of 1103 branches covered (79.42%)

Branch coverage included in aggregate %.

3452 of 3452 new or added lines in 44 files covered. (100.0%)

6647 of 7185 relevant lines covered (92.51%)

6.69 hits per line

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

87.34
/src/pages/settings/Security.tsx
1
import { ICON_SIZE } from "@src/constants/icons";
2✔
2
import { Fragment, useEffect, useState } from "react";
2✔
3
import { useTranslation } from "react-i18next";
2✔
4
import { FiChevronDown, FiChevronLeft, FiChevronUp } from "react-icons/fi";
2✔
5
import { useNavigate } from "react-router-dom";
2✔
6
import { PageWrapper, Loading, LoadingButton } from "@src/components/common";
2✔
7
import { useToast, useLoading } from "@src/hooks";
2✔
8
import Extension from "@src/Extension";
2✔
9
import { BsEye, BsTrash } from "react-icons/bs";
2✔
10
import { RESTORE_PASSWORD } from "@src/routes/paths";
2✔
11
import { Dialog, Transition } from "@headlessui/react";
2✔
12
import { useAccountContext } from "@src/providers";
2✔
13
import Account from "@src/storage/entities/Account";
2✔
14

2✔
15
export const Security = () => {
2✔
16
  const { t } = useTranslation("security");
10✔
17
  const { t: tCommon } = useTranslation("common");
10✔
18
  const { getSelectedAccount } = useAccountContext();
10✔
19
  const {
10✔
20
    isLoading: isLoadingReset,
10✔
21
    starLoading: startLoadingReset,
10✔
22
    endLoading: endLoadingReset,
10✔
23
  } = useLoading();
10✔
24
  const [isLoading, setIsLoading] = useState(true);
10✔
25
  const [sites, setSites] = useState([] as string[]);
10✔
26
  const [showSites, setShowSites] = useState(false);
10✔
27
  const [search, setSearch] = useState("" as string);
10✔
28
  const [isOpen, setIsOpen] = useState(false);
10✔
29
  const [isResetOpen, setIsResetOpen] = useState(false);
10✔
30
  const [privateKey, setPrivateKey] = useState("" as string);
10✔
31
  const [hideKeys, setHideKeys] = useState(true);
10✔
32
  const [account, setAccount] = useState(
10✔
33
    undefined as Account | null | undefined
10✔
34
  );
10✔
35
  const { showErrorToast } = useToast();
10✔
36
  const navigate = useNavigate();
10✔
37

10✔
38
  useEffect(() => {
10✔
39
    setIsLoading(true);
4✔
40
    getSites();
4✔
41
    getAccount();
4✔
42
    getPrivateKeyOrSeed();
4✔
43
  }, []);
10✔
44

10✔
45
  const openModal = () => {
10✔
46
    setIsOpen(true);
×
47
    setHideKeys(true);
×
48
  };
×
49

10✔
50
  const closeModal = () => {
10✔
51
    setIsOpen(false);
×
52
    setHideKeys(true);
×
53
  };
×
54

10✔
55
  const openResetModal = () => {
10✔
56
    setIsResetOpen(true);
×
57
  };
×
58

10✔
59
  const closeResetModal = () => {
10✔
60
    setIsResetOpen(false);
×
61
  };
×
62

10✔
63
  const resetWallet = async () => {
10✔
64
    startLoadingReset();
×
65
    try {
×
66
      await Extension.resetWallet();
×
67
      window.location.reload();
×
68
    } catch (error) {
×
69
      showErrorToast(tCommon(error as string));
×
70
      endLoadingReset();
×
71
    }
×
72
  };
×
73

10✔
74
  const toggleShowSites = () => {
10✔
75
    setShowSites(!showSites);
2✔
76
  };
2✔
77

10✔
78
  const getAccount = async () => {
10✔
79
    try {
4✔
80
      const account = await getSelectedAccount();
4✔
81
      setAccount(account);
4✔
82
    } catch (error) {
4!
83
      showErrorToast(tCommon(error as string));
×
84
    }
×
85
  };
4✔
86

10✔
87
  const getPrivateKeyOrSeed = async (): Promise<void> => {
10✔
88
    try {
4✔
89
      let privateKeyOrSeed: string | undefined =
4✔
90
        await Extension.showPrivateKey();
4✔
91
      if (!privateKeyOrSeed) {
4✔
92
        privateKeyOrSeed = await Extension.showSeed();
4✔
93
      }
4✔
94
      if (!privateKeyOrSeed) {
4!
95
        throw new Error("no_private_key_or_seed");
×
96
      }
×
97
      setPrivateKey(privateKeyOrSeed);
4✔
98
    } catch (error: unknown) {
4!
99
      if (typeof error === "string") {
×
100
        showErrorToast(tCommon(error));
×
101
      }
×
102
    }
×
103
  };
4✔
104

10✔
105
  const getSites = async () => {
10✔
106
    try {
4✔
107
      const sites = await Extension.getTrustedSites();
4✔
108
      setSites(sites);
4✔
109
    } catch (error) {
4!
110
      setSites([]);
×
111
      showErrorToast(tCommon(error as string));
×
112
    } finally {
4✔
113
      setIsLoading(false);
4✔
114
    }
4✔
115
  };
4✔
116

10✔
117
  const removeSite = async (site: string) => {
10✔
118
    try {
×
119
      await Extension.removeTrustedSite(site);
×
120
      getSites();
×
121
    } catch (error) {
×
122
      showErrorToast(tCommon(error as string));
×
123
    }
×
124
  };
×
125

10✔
126
  if (isLoading) {
10✔
127
    return <Loading />;
4✔
128
  }
4✔
129

6✔
130
  return (
6✔
131
    <PageWrapper>
6✔
132
      <div className="flex items-center gap-3 mb-10">
6✔
133
        <FiChevronLeft
6✔
134
          className="cursor-pointer"
6✔
135
          size={ICON_SIZE}
6✔
136
          onClick={() => navigate(-1)}
6✔
137
        />
6✔
138
        <p className="font-medium text-2xl">{t("title")}</p>
6✔
139
      </div>
6✔
140
      <div className="flex flex-col mt-5 gap-2">
6✔
141
        <div className="flex justify-between items-center">
6✔
142
          <p className="text-lg font-medium">{t("trusted_sites")}</p>
6✔
143
          <div className="p-2" data-testid="show-sites">
6✔
144
            {showSites ? (
6✔
145
              <FiChevronUp
2✔
146
                className="cursor-pointer"
2✔
147
                size={ICON_SIZE}
2✔
148
                onClick={toggleShowSites}
2✔
149
              />
2✔
150
            ) : (
4✔
151
              <FiChevronDown
4✔
152
                className="cursor-pointer"
4✔
153
                size={ICON_SIZE}
4✔
154
                onClick={toggleShowSites}
4✔
155
              />
4✔
156
            )}
10✔
157
          </div>
10✔
158
        </div>
10✔
159
        {showSites && (
10✔
160
          <>
2✔
161
            <input
2✔
162
              id="search"
2✔
163
              placeholder={t("search") || "Search"}
2!
164
              className=" border text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white"
2✔
165
              onChange={(e) => {
2✔
166
                setSearch(e.target.value);
×
167
              }}
×
168
            />
2✔
169
            {sites
2✔
170
              .filter((site) =>
2✔
171
                site.toLowerCase().includes(search.toLowerCase())
4✔
172
              )
2✔
173
              .map((site, index) => (
2✔
174
                <div
4✔
175
                  className="flex justify-between items-center hover:bg-custom-green-bg hover:bg-opacity-40 rounded-xl px-3 py-3 cursor-pointer gap-2"
4✔
176
                  key={index}
4✔
177
                >
4✔
178
                  <p className="text-custom-green-bg px-2 break-all w-[75%]">
4✔
179
                    {site}
4✔
180
                  </p>
4✔
181
                  <div className="w-[20%] flex justify-end">
4✔
182
                    <BsTrash
4✔
183
                      className="text-lg hover:text-custom-red-bg"
4✔
184
                      onClick={() => removeSite(site)}
4✔
185
                    />
4✔
186
                  </div>
4✔
187
                </div>
4✔
188
              ))}
2✔
189
            {sites.length === 0 && (
2!
190
              <div className="flex justify-center items-center px-4 py-2">
×
191
                <p className="text-sm text-gray-400">{t("no_trusted_sites")}</p>
×
192
              </div>
×
193
            )}
2✔
194
          </>
2✔
195
        )}
10✔
196
        <div className="flex justify-between items-center">
10✔
197
          <p className="text-lg font-medium">{t("credentials")}</p>
10✔
198
          <div className="p-2">
10✔
199
            <button
10✔
200
              type="button"
10✔
201
              className="inline-flex justify-between items-center cursor-pointer rounded-md border border-transparent hover:bg-gray-400 hover:bg-opacity-30 px-4 py-2 text-sm font-medium"
10✔
202
              onClick={() => navigate(RESTORE_PASSWORD)}
10✔
203
            >
10✔
204
              {t("restore_password")}
10✔
205
            </button>
10✔
206
          </div>
10✔
207
        </div>
10✔
208
        <div className="flex justify-between items-center">
10✔
209
          <p className="text-lg font-medium">{t("show_account_keys")}</p>
10✔
210
          <div className="p-2">
10✔
211
            <div className="relative inset-0 flex items-center justify-center">
10✔
212
              <button
10✔
213
                type="button"
10✔
214
                onClick={openModal}
10✔
215
                className="inline-flex justify-between items-center cursor-pointer rounded-md border border-transparent hover:bg-gray-400 hover:bg-opacity-30 px-4 py-2 text-sm font-medium"
10✔
216
              >
10✔
217
                {t("show")}
10✔
218
              </button>
10✔
219
            </div>
10✔
220
            <Transition appear show={isOpen} as={Fragment}>
10✔
221
              <Dialog as="div" className="relative z-10" onClose={closeModal}>
10✔
222
                <Transition.Child
10✔
223
                  as={Fragment}
10✔
224
                  enter="ease-out duration-300"
10✔
225
                  enterFrom="opacity-0"
10✔
226
                  enterTo="opacity-100"
10✔
227
                  leave="ease-in duration-200"
10✔
228
                  leaveFrom="opacity-100"
10✔
229
                  leaveTo="opacity-0"
10✔
230
                >
10✔
231
                  <div className="fixed inset-0 bg-black bg-opacity-25" />
10✔
232
                </Transition.Child>
10✔
233
                <div className="fixed inset-0 overflow-y-auto">
10✔
234
                  <div className="flex min-h-full items-center justify-center p-4 text-center">
10✔
235
                    <Transition.Child
10✔
236
                      as={Fragment}
10✔
237
                      enter="ease-out duration-300"
10✔
238
                      enterFrom="opacity-0 scale-95"
10✔
239
                      enterTo="opacity-100 scale-100"
10✔
240
                      leave="ease-in duration-200"
10✔
241
                      leaveFrom="opacity-100 scale-100"
10✔
242
                      leaveTo="opacity-0 scale-95"
10✔
243
                    >
10✔
244
                      <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-custom-gray-bg p-6 text-left align-middle shadow-xl transition-all">
10✔
245
                        <Dialog.Title
10✔
246
                          as="h3"
10✔
247
                          className="text-lg font-medium leading-6 text-white"
10✔
248
                        >
10✔
249
                          {account?.value.name}
10!
250
                        </Dialog.Title>
10✔
251
                        <div className="flex flex-col mt-4">
10✔
252
                          <p className="text-sm text-white text-center break-all rounded-lg border p-2 bg-custom-gray-bg border-custom-green-bg">
10✔
253
                            {account?.value.address}
10!
254
                          </p>
10✔
255
                          <div className="relative my-8">
10✔
256
                            <textarea
10✔
257
                              className="text-sm h-[100px] rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 bg-gray-700 border-gray-600 placeholder-gray-400 text-white resize-none relative select-none"
10✔
258
                              value={privateKey}
10✔
259
                              aria-readonly={true}
10✔
260
                              readOnly={true}
10✔
261
                              disabled
10✔
262
                              onSelect={(e) => e.preventDefault()}
10✔
263
                              onCopy={(e) => e.preventDefault()}
10✔
264
                            />
10✔
265
                            {hideKeys && (
10✔
266
                              <div className="absolute left-0 top-0 w-full h-full bg-transparent backdrop-blur-sm rounded-lg flex justify-center items-center">
6✔
267
                                <button
6✔
268
                                  className="flex flex-col items-center"
6✔
269
                                  onClick={() => setHideKeys(false)}
6✔
270
                                >
6✔
271
                                  <p>{t("show_account_keys")}</p>
6✔
272
                                  <BsEye size={18} />
6✔
273
                                </button>
6✔
274
                              </div>
6✔
275
                            )}
10✔
276
                          </div>
10✔
277
                          <p className="text-sm font-medium text-custom-red-bg">
10✔
278
                            {t("account_keys_description")}
10✔
279
                          </p>
10✔
280
                          <div className="mt-4 flex justify-end">
10✔
281
                            <button
10✔
282
                              type="button"
10✔
283
                              onClick={closeModal}
10✔
284
                              className="inline-flex justify-between items-center cursor-pointer rounded-md border border-custom-green-bg hover:bg-custom-green-bg px-4 py-2 text-sm font-medium"
10✔
285
                            >
10✔
286
                              {tCommon("close")}
10✔
287
                            </button>
10✔
288
                          </div>
10✔
289
                        </div>
10✔
290
                      </Dialog.Panel>
10✔
291
                    </Transition.Child>
10✔
292
                  </div>
10✔
293
                </div>
10✔
294
              </Dialog>
10✔
295
            </Transition>
10✔
296
          </div>
10✔
297
        </div>
10✔
298
        <div className="flex justify-between items-center">
10✔
299
          <p className="text-lg font-medium">{t("reset_wallet")}</p>
10✔
300
          <div className="p-2">
10✔
301
            <div className="relative inset-0 flex items-center justify-center">
10✔
302
              <button
10✔
303
                type="button"
10✔
304
                onClick={openResetModal}
10✔
305
                className="inline-flex justify-between items-center cursor-pointer rounded-md border border-transparent hover:bg-custom-red-bg px-4 py-2 text-sm font-medium"
10✔
306
              >
10✔
307
                {t("reset")}
10✔
308
              </button>
10✔
309
            </div>
10✔
310
            <Transition appear show={isResetOpen} as={Fragment}>
10✔
311
              <Dialog
10✔
312
                as="div"
10✔
313
                className="relative z-10"
10✔
314
                onClose={!isLoadingReset ? closeResetModal : () => null}
10!
315
              >
10✔
316
                <Transition.Child
10✔
317
                  as={Fragment}
10✔
318
                  enter="ease-out duration-300"
10✔
319
                  enterFrom="opacity-0"
10✔
320
                  enterTo="opacity-100"
10✔
321
                  leave="ease-in duration-200"
10✔
322
                  leaveFrom="opacity-100"
10✔
323
                  leaveTo="opacity-0"
10✔
324
                >
10✔
325
                  <div className="fixed inset-0 bg-black bg-opacity-25" />
10✔
326
                </Transition.Child>
10✔
327
                <div className="fixed inset-0 overflow-y-auto">
10✔
328
                  <div className="flex min-h-full items-center justify-center p-4 text-center">
10✔
329
                    <Transition.Child
10✔
330
                      as={Fragment}
10✔
331
                      enter="ease-out duration-300"
10✔
332
                      enterFrom="opacity-0 scale-95"
10✔
333
                      enterTo="opacity-100 scale-100"
10✔
334
                      leave="ease-in duration-200"
10✔
335
                      leaveFrom="opacity-100 scale-100"
10✔
336
                      leaveTo="opacity-0 scale-95"
10✔
337
                    >
10✔
338
                      <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-custom-gray-bg p-6 text-left align-middle shadow-xl transition-all">
10✔
339
                        <Dialog.Title
10✔
340
                          as="h3"
10✔
341
                          className="text-lg font-medium leading-6 text-white"
10✔
342
                        >
10✔
343
                          {t("reset_wallet")}
10✔
344
                        </Dialog.Title>
10✔
345
                        <div className="flex flex-col mt-4">
10✔
346
                          <p className="text-sm font-medium text-custom-red-bg">
10✔
347
                            {t("reset_wallet_warning")}
10✔
348
                          </p>
10✔
349
                          <div className="mt-4 flex justify-end">
10✔
350
                            <LoadingButton
10✔
351
                              isLoading={isLoadingReset}
10✔
352
                              onClick={resetWallet}
10✔
353
                              classname="inline-flex justify-between items-center cursor-pointer rounded-md border border-custom-red-bg hover:bg-custom-red-bg px-4 py-2 text-sm font-medium"
10✔
354
                            >
10✔
355
                              {t("reset")}
10✔
356
                            </LoadingButton>
10✔
357
                          </div>
10✔
358
                        </div>
10✔
359
                      </Dialog.Panel>
10✔
360
                    </Transition.Child>
10✔
361
                  </div>
10✔
362
                </div>
10✔
363
              </Dialog>
10✔
364
            </Transition>
10✔
365
          </div>
10✔
366
        </div>
10✔
367
      </div>
10✔
368
    </PageWrapper>
10✔
369
  );
10✔
370
};
4✔
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