commit 5b845c0b5853ff9c3a4bf49c13b7d2245f4deb7f
parent 4309bf921bff776329a4084932c89eb4f154c3fe
Author: Sebastian <sebasjm@taler-systems.com>
Date: Tue, 10 Feb 2026 07:44:36 -0300
first int test with browser
Diffstat:
10 files changed, 377 insertions(+), 5 deletions(-)
diff --git a/packages/taler-harness/README.md b/packages/taler-harness/README.md
@@ -11,3 +11,42 @@ To get more actionable stack traces, enable source maps for node:
```
export NODE_OPTIONS=--enable-source-maps
```
+
+## Headless Web Integration test
+
+1) First you require the browser that you are going to test with
+
+Puppeteer scripts can be used to download
+
+```
+npx @puppeteer/browsers install firefox@stable
+npx @puppeteer/browsers install chrome@stable
+
+npx @puppeteer/browsers install firefox@beta
+npx @puppeteer/browsers install chrome@beta
+
+npx @puppeteer/browsers install firefox@144
+npx @puppeteer/browsers install chrome@144
+```
+
+Or can be download manually from
+
+Firefox: https://archive.mozilla.org/pub/firefox/
+Chrome: https://googlechromelabs.github.io/chrome-for-testing/
+
+let BROWSER_DIR be the directory where the browser was downloaded
+
+2) Then you need the respective webdriver binaries
+
+Firefox: https://github.com/mozilla/geckodriver/
+Chrome: https://googlechromelabs.github.io/chrome-for-testing/
+
+let DRIVER_DIR be the directory where the browser was downloaded
+
+3) The integration tests will expect the driver+browser available on port 9090 for chrome and 9091 for firefox
+
+Firefox: $DRIVER_DIR/geckodriver --port 9091 -b $BROWSER_DIR/firefox
+Chrome: PATH=$DRIVER_DIR $DRIVER_DIR/chromedriver --port=9090 --verbose
+
+
+
diff --git a/packages/taler-harness/package.json b/packages/taler-harness/package.json
@@ -41,6 +41,8 @@
"@gnu-taler/taler-util": "workspace:*",
"@gnu-taler/taler-wallet-core": "workspace:*",
"postgres": "^3.4.5",
+ "selenium-webdriver": "4.40.0",
+ "@types/selenium-webdriver": "4.35.5",
"tslib": "^2.6.2"
}
}
diff --git a/packages/taler-harness/src/harness/environments.ts b/packages/taler-harness/src/harness/environments.ts
@@ -93,6 +93,15 @@ import {
} from "./harness.js";
import * as fs from "node:fs";
+import {
+ By,
+ Builder,
+ Browser,
+ chromium,
+ ThenableWebDriver,
+} from "selenium-webdriver";
+import * as Chrome from "selenium-webdriver/chrome.js";
+import * as Firefox from "selenium-webdriver/firefox.js";
const logger = new Logger("helpers.ts");
@@ -137,6 +146,7 @@ export interface SimpleTestEnvironmentNg3 {
merchantAdminAccessToken: AccessToken;
walletClient: WalletClient;
walletService: WalletService;
+ createBrowser: (params: { firefox?: boolean }) => Promise<ThenableWebDriver>;
}
export interface EnvOptions {
@@ -476,6 +486,24 @@ export async function createSimpleTestkudosEnvironmentV2(
};
}
+export async function createBrowser({ firefox }: { firefox?: boolean }): Promise<ThenableWebDriver> {
+ const chromeOpts = new Chrome.Options();
+ chromeOpts.addArguments("--no-sandbox", "-headless");
+
+ const chromeDriver = new Builder()
+ .forBrowser(Browser.CHROME)
+ .setChromeOptions(chromeOpts)
+ .usingServer("http://127.0.0.1:9090/");
+ const firefoxOpts = new Firefox.Options();
+ firefoxOpts.addArguments("--headless");
+
+ const firefoxDriver = new Builder()
+ .forBrowser(Browser.FIREFOX)
+ .setFirefoxOptions(firefoxOpts)
+ .usingServer("http://127.0.0.1:9091/");
+ return firefox ? firefoxDriver.build() : chromeDriver.build();
+}
+
/**
* Run a test case with a simple TESTKUDOS Taler environment, consisting
* of one exchange, one bank and one merchant.
@@ -642,6 +670,7 @@ export async function createSimpleTestkudosEnvironmentV3(
bank,
bankClient,
exchangeBankAccount,
+ createBrowser,
};
}
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
@@ -1187,6 +1187,7 @@ export interface ExchangeServiceInterface {
}
export class ExchangeService implements ExchangeServiceInterface {
+
static fromExistingConfig(
gc: GlobalTestState,
exchangeName: string,
diff --git a/packages/taler-harness/src/integrationtests/test-fee-regression.ts b/packages/taler-harness/src/integrationtests/test-fee-regression.ts
@@ -24,6 +24,7 @@ import {
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
SimpleTestEnvironmentNg3,
+ createBrowser,
createWalletDaemonWithClient,
makeTestPaymentV2,
withdrawViaBankV3,
@@ -197,6 +198,7 @@ export async function createMyTestkudosEnvironment(
walletService,
bankClient,
bank,
+ createBrowser,
exchangeBankAccount,
};
}
diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts
@@ -25,6 +25,7 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig } from "../harness/denomStructures.js";
import {
SimpleTestEnvironmentNg3,
+ createBrowser,
createWalletDaemonWithClient,
makeTestPaymentV2,
withdrawViaBankV3,
@@ -185,6 +186,7 @@ async function createTestEnvironment(
bank,
bankClient,
exchangeBankAccount,
+ createBrowser
};
}
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -217,6 +217,7 @@ import { runWithdrawalIdempotentTest } from "./test-withdrawal-idempotent.js";
import { runWithdrawalManualTest } from "./test-withdrawal-manual.js";
import { runWithdrawalPrepareTest } from "./test-withdrawal-prepare.js";
import { runMerchantBankBadWireTargetTest } from "./test-merchant-bank-bad-wire-target.js";
+import { runWebMerchantSelfProvisionActivationTest } from "./web/test-merchant-self-provision-activation.js";
/**
* Test runner.
@@ -335,6 +336,7 @@ const allTests: TestMainFunction[] = [
runExchangeMasterPubChangeTest,
runMerchantCategoriesTest,
runMerchantSelfProvisionActivationTest,
+ runWebMerchantSelfProvisionActivationTest,
runMerchantSelfProvisionActivationAndLoginTest,
runMerchantSelfProvisionForgotPasswordTest,
runMerchantSelfProvisionInactiveAccountPermissionsTest,
diff --git a/packages/taler-harness/src/integrationtests/web/test-merchant-self-provision-activation.ts b/packages/taler-harness/src/integrationtests/web/test-merchant-self-provision-activation.ts
@@ -0,0 +1,253 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ alternativeOrThrow,
+ Duration,
+ HttpStatusCode,
+ LoginTokenScope,
+ MerchantAuthMethod,
+ succeedOrThrow,
+ TalerMerchantInstanceHttpClient,
+ TalerMerchantManagementHttpClient,
+ TanChannel,
+} from "@gnu-taler/taler-util";
+import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
+import { startTanHelper } from "harness/tan-helper.js";
+import { randomBytes } from "node:crypto";
+import { chmodSync, writeFileSync } from "node:fs";
+import { GlobalTestState, MERCHANT_DEFAULT_AUTH } from "../../harness/harness.js";
+import { solveMFA } from "../test-merchant-self-provision-inactive-account-permissions.js";
+import { By, locateWith } from "selenium-webdriver";
+
+/**
+ * Do basic checks on instance management and authentication.
+ */
+export async function runWebMerchantSelfProvisionActivationTest(
+ t: GlobalTestState,
+) {
+ // Set up test environment
+
+ // FIXME: maybe merchant can use commands?
+ const RND = randomBytes(10).toString("hex");
+ const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
+ const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
+ writeFileSync(
+ helperScript,
+ `#!/bin/bash
+taler-harness run-helper --socket ${socketFile} -- $@
+`,
+ );
+ chmodSync(helperScript, "777");
+
+ const {
+ walletClient,
+ bankClient,
+ exchange,
+ merchant,
+ bank,
+ merchantAdminAccessToken,
+ createBrowser
+ } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
+ additionalMerchantConfig(m) {
+ m.modifyConfig(async (cfg) => {
+ cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
+ cfg.setString("merchant", "HELPER_SMS", helperScript);
+ cfg.setString("merchant", "HELPER_EMAIL", helperScript);
+ cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email sms");
+ });
+ },
+ });
+ const helper = await startTanHelper({ socketFile });
+
+ const browser = await createBrowser({})
+
+ await browser.get(merchant.makeInstanceBaseUrl());
+
+ const title = await browser.getTitle();
+
+ t.assertDeepEqual("GNU Taler Merchant Backoffice", title);
+
+ await browser.manage().setTimeouts({ implicit: 2000, });
+
+ // const noAsideWhenLogout = await browser.findElement(By.css('aside'))
+ // t.assertDeepEqual(noAsideWhenLogout, undefined)
+
+ // browser.manage()
+ const form = await browser.findElement(By.css('form'))
+ const username = await form.findElement(By.css("input[name=username]"));
+ const password = await form.findElement(By.css("input[name=token]"));
+ const submit = await form.findElement(By.css('button[type=submit]'));
+
+ await username.sendKeys("admin");
+ await password.sendKeys(MERCHANT_DEFAULT_AUTH.password);
+ await submit.click();
+
+ await browser.sleep(100)
+
+ const nousername = await browser.findElement(By.css("aside"));
+ t.assertTrue(nousername !== undefined)
+
+ const asd = await browser.takeScreenshot()
+ console.log(asd)
+ await browser.quit();
+
+ // create instance with self provision
+
+ // const instanceInfo = {
+ // id: "self-instance",
+ // name: "My instance",
+ // auth: {
+ // method: MerchantAuthMethod.TOKEN,
+ // password: "123",
+ // },
+ // default_pay_delay: Duration.toTalerProtocolDuration(
+ // Duration.fromSpec({ days: 14 }),
+ // ),
+ // default_wire_transfer_delay: Duration.toTalerProtocolDuration(
+ // Duration.fromSpec({ days: 14 }),
+ // ),
+ // jurisdiction: {},
+ // address: {},
+ // email: "some@taler.net",
+ // phone_number: "+1111",
+ // use_stefan: false,
+ // };
+ // const signupStart = alternativeOrThrow(
+ // await merchantClient.createInstanceSelfProvision(instanceInfo),
+ // HttpStatusCode.Accepted,
+ // );
+
+ // creation requires 2fa
+ // t.assertDeepEqual(signupStart.challenges.length, 2);
+ // t.assertDeepEqual(signupStart.combi_and, true);
+
+ // const firstChallenge = signupStart.challenges[0];
+ // const secondChallenge = signupStart.challenges[1];
+
+ // //FIXME: check the order
+ // // always first emails since is cheaper
+ // // t.assertDeepEqual(firstChallenge.challenge_type, TanChannel.EMAIL);
+ // // t.assertDeepEqual(secondChallenge.challenge_type, TanChannel.SMS);
+
+ // {
+ // // new instance is ping, then is not listed
+ // const r = succeedOrThrow(
+ // await merchantClient.listInstances(merchantAdminAccessToken),
+ // );
+ // t.assertDeepEqual(r.instances.length, 2);
+ // }
+
+ // {
+ // succeedOrThrow(
+ // await merchantClient.sendChallenge(firstChallenge.challenge_id),
+ // );
+
+ // const message = helper.getLastCodeForAddress(instanceInfo.phone_number);
+ // const [tanCode] = message.split("\n");
+ // succeedOrThrow(
+ // await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
+ // tan: tanCode,
+ // }),
+ // );
+ // }
+
+ // {
+ // succeedOrThrow(
+ // await merchantClient.sendChallenge(secondChallenge.challenge_id),
+ // );
+
+ // const message = helper.getLastCodeForAddress(instanceInfo.email);
+ // const [tanCode] = message.split("\n");
+ // succeedOrThrow(
+ // await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
+ // tan: tanCode,
+ // }),
+ // );
+ // }
+
+ // const completeSignup = await merchantClient.createInstanceSelfProvision(
+ // instanceInfo,
+ // {
+ // challengeIds: [firstChallenge.challenge_id, secondChallenge.challenge_id],
+ // },
+ // );
+
+ // t.assertDeepEqual(completeSignup.type, "ok");
+
+ // const instanceApi = new TalerMerchantInstanceHttpClient(
+ // merchantClient.getSubInstanceAPI(instanceInfo.id),
+ // merchantClient.httpLib,
+ // );
+
+ // {
+ // // new instance is completed, now it should be visible
+ // const r = succeedOrThrow(
+ // await merchantClient.listInstances(merchantAdminAccessToken),
+ // );
+ // t.assertDeepEqual(r.instances.length, 3);
+ // }
+
+ // const loginChallenge = alternativeOrThrow(
+ // await instanceApi.createAccessToken(
+ // instanceInfo.id,
+ // instanceInfo.auth.password,
+ // {
+ // scope: LoginTokenScope.All,
+ // },
+ // ),
+ // HttpStatusCode.Accepted,
+ // );
+
+ // await solveMFA(merchantClient, helper, loginChallenge, {
+ // [TanChannel.EMAIL]: instanceInfo.email,
+ // [TanChannel.SMS]: instanceInfo.phone_number,
+ // });
+
+ // const { access_token: token } = succeedOrThrow(
+ // await instanceApi.createAccessToken(
+ // instanceInfo.id,
+ // instanceInfo.auth.password,
+ // {
+ // scope: LoginTokenScope.All,
+ // },
+ // {
+ // challengeIds: loginChallenge.challenges.map((c) => c.challenge_id),
+ // },
+ // ),
+ // );
+
+ // const det = succeedOrThrow(
+ // await instanceApi.getCurrentInstanceDetails(token),
+ // );
+
+ // // check that the instance has the new email
+ // t.assertDeepEqual(det.email, instanceInfo.email);
+ // t.assertDeepEqual(det.email_validated, true);
+ // t.assertDeepEqual(det.phone_number, instanceInfo.phone_number);
+ // t.assertDeepEqual(det.phone_validated, true);
+
+ helper.stop();
+}
+
+runWebMerchantSelfProvisionActivationTest.suites = [
+ "web",
+ "merchant",
+ "self-provision",
+];
diff --git a/packages/taler-harness/tsconfig.json b/packages/taler-harness/tsconfig.json
@@ -4,7 +4,9 @@
"lib": ["ES2020"],
"outDir": "lib",
"rootDir": "src",
- "baseUrl": "./src"
+ "baseUrl": "./src",
+ "skipLibCheck": true /* Skip type checking of declaration files. */
+
},
"include": ["src/**/*"],
"references": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
@@ -837,9 +837,15 @@ importers:
'@gnu-taler/taler-wallet-core':
specifier: workspace:*
version: link:../taler-wallet-core
+ '@types/selenium-webdriver':
+ specifier: 4.35.5
+ version: 4.35.5
postgres:
specifier: ^3.4.5
version: 3.4.5
+ selenium-webdriver:
+ specifier: 4.40.0
+ version: 4.40.0
tslib:
specifier: ^2.6.2
version: 2.6.2
@@ -2461,6 +2467,9 @@ packages:
resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
engines: {node: '>=6.9.0'}
+ '@bazel/runfiles@6.5.0':
+ resolution: {integrity: sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==}
+
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
@@ -3173,6 +3182,9 @@ packages:
'@types/retry@0.12.0':
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
+ '@types/selenium-webdriver@4.35.5':
+ resolution: {integrity: sha512-wCQCjWmahRkUAO7S703UAvBFkxz4o/rjX4T2AOSWKXSi0sTQPsrXxR0GjtFUT0ompedLkYH4R5HO5Urz0hyeog==}
+
'@types/semver@7.3.12':
resolution: {integrity: sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==}
@@ -5896,24 +5908,25 @@ packages:
glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@6.0.4:
resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
glob@7.2.0:
resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
- deprecated: Glob versions prior to v9 are no longer supported
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
global-dirs@3.0.0:
resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==}
@@ -8947,6 +8960,10 @@ packages:
select-hose@2.0.0:
resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==}
+ selenium-webdriver@4.40.0:
+ resolution: {integrity: sha512-dU0QbnVKdPmoNP8OtMCazRdtU2Ux6Wl4FEpG1iwUbDeajJK1dBAywBLrC1D7YFRtogHzN96AbXBgBAJaarcysw==}
+ engines: {node: '>= 20.0.0'}
+
selfsigned@2.1.1:
resolution: {integrity: sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==}
engines: {node: '>=10'}
@@ -9573,6 +9590,10 @@ packages:
resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
engines: {node: '>=8.17.0'}
+ tmp@0.2.5:
+ resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
+ engines: {node: '>=14.14'}
+
to-arraybuffer@1.0.1:
resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==}
@@ -12735,6 +12756,8 @@ snapshots:
'@babel/helper-validator-identifier': 7.22.20
to-fast-properties: 2.0.0
+ '@bazel/runfiles@6.5.0': {}
+
'@bcoe/v8-coverage@0.2.3': {}
'@creativebulma/bulma-tooltip@1.2.0': {}
@@ -13567,6 +13590,11 @@ snapshots:
'@types/retry@0.12.0': {}
+ '@types/selenium-webdriver@4.35.5':
+ dependencies:
+ '@types/node': 20.11.13
+ '@types/ws': 8.5.3
+
'@types/semver@7.3.12': {}
'@types/semver@7.5.6': {}
@@ -20504,6 +20532,16 @@ snapshots:
select-hose@2.0.0: {}
+ selenium-webdriver@4.40.0:
+ dependencies:
+ '@bazel/runfiles': 6.5.0
+ jszip: 3.10.1
+ tmp: 0.2.5
+ ws: 8.18.3
+ transitivePeerDependencies:
+ - bufferutil
+ - utf-8-validate
+
selfsigned@2.1.1:
dependencies:
node-forge: 1.3.1
@@ -21305,6 +21343,8 @@ snapshots:
dependencies:
rimraf: 3.0.2
+ tmp@0.2.5: {}
+
to-arraybuffer@1.0.1: {}
to-fast-properties@2.0.0: {}