mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
Add unit & integration testing
* Add Cypress component testing * Add SeedAddress component testing * Add Error component test * Add Search component test * Add code coverage instrumentation * Add BranchSelector component testing * Add coverage check to CI * Require node version 14 Signed-off-by: Sebastian Martinez <sebastinez@me.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
name: check-components
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: '14'
|
||||||
|
- run: npm ci
|
||||||
|
- run: ./scripts/test
|
||||||
|
shell: bash
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
build/
|
build/
|
||||||
node_modules/
|
node_modules/
|
||||||
NOTES
|
NOTES
|
||||||
|
|
||||||
|
# Coverage
|
||||||
|
.nyc_output
|
||||||
|
coverage
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"all": true,
|
||||||
|
"extension": [
|
||||||
|
".svelte",
|
||||||
|
".ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"video": false,
|
||||||
|
"componentFolder": "src",
|
||||||
|
"testFiles": "**/*spec.{js,ts}",
|
||||||
|
"pluginsFile": "cypress/plugins/index.cjs",
|
||||||
|
"baseUrl": "http://localhost:3000"
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const { startDevServer } = require('@cypress/vite-dev-server');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = (on, config) => {
|
||||||
|
on('dev-server:start', (options) => {
|
||||||
|
return startDevServer({
|
||||||
|
options,
|
||||||
|
viteConfig: {
|
||||||
|
configFile: path.resolve(__dirname, '..', '..', 'vite.config.ts'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
require('@cypress/code-coverage/task')(on, config);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import '@cypress/code-coverage/support';
|
||||||
|
import '@testing-library/cypress/add-commands';
|
||||||
|
|
||||||
|
export const styles = {
|
||||||
|
cssFile: "public/index.css",
|
||||||
|
style: `
|
||||||
|
.wrapper {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
html: `
|
||||||
|
<div class="wrapper">
|
||||||
|
<div id="here" />
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
};
|
||||||
Generated
+5817
-5349
File diff suppressed because it is too large
Load Diff
+14
-6
@@ -5,20 +5,28 @@
|
|||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
"build": "scripts/build",
|
"build": "scripts/build",
|
||||||
"check": "scripts/check",
|
"check": "scripts/check",
|
||||||
"test": "echo \"This template does not include a test runner by default.\" && exit 1"
|
"test": "cypress run-ct"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "1.0.0-next.10",
|
"@cypress/code-coverage": "^3.9.12",
|
||||||
"@tsconfig/svelte": "^1.0.10",
|
"@cypress/vite-dev-server": "^2.2.2",
|
||||||
"@types/node": "^15.12.2",
|
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.36",
|
||||||
|
"@testing-library/cypress": "^8.0.2",
|
||||||
|
"@tsconfig/svelte": "^1.0.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.26.1",
|
"@typescript-eslint/eslint-plugin": "^4.26.1",
|
||||||
"@typescript-eslint/parser": "^4.26.1",
|
"@typescript-eslint/parser": "^4.26.1",
|
||||||
|
"cypress": "^9.3.1",
|
||||||
|
"cypress-svelte-unit-test": "git+https://github.com/flakolefluk/cypress-svelte-unit-test.git",
|
||||||
"eslint": "^7.28.0",
|
"eslint": "^7.28.0",
|
||||||
"eslint-plugin-svelte3": "^3.2.0",
|
"eslint-plugin-svelte3": "^3.2.0",
|
||||||
"svelte-check": "^1.5.2",
|
"svelte-check": "^1.5.2",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.2.4",
|
||||||
"vite": "^2.3.7",
|
"vite": "^2.7.13",
|
||||||
|
"vite-plugin-istanbul": "^2.4.0",
|
||||||
"vite-plugin-rewrite-all": "^0.1.2"
|
"vite-plugin-rewrite-all": "^0.1.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -44,7 +52,7 @@
|
|||||||
"multibase": "^4.0.4",
|
"multibase": "^4.0.4",
|
||||||
"multihashes": "^4.0.2",
|
"multihashes": "^4.0.2",
|
||||||
"pure-svg-code": "^1.0.6",
|
"pure-svg-code": "^1.0.6",
|
||||||
"svelte": "^3.32.3",
|
"svelte": "^3.44.3",
|
||||||
"svelte-preprocess": "^4.7.2",
|
"svelte-preprocess": "^4.7.2",
|
||||||
"svelte-routing": "^1.6.0"
|
"svelte-routing": "^1.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# For now we only check the Svelte files.
|
||||||
|
|
||||||
|
npm test
|
||||||
|
npx nyc report
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import Error from "./Error.svelte";
|
||||||
|
import { mount } from "cypress-svelte-unit-test";
|
||||||
|
import { Failure } from '@app/error';
|
||||||
|
import { styles } from "@test/support/index";
|
||||||
|
|
||||||
|
describe('Error', function () {
|
||||||
|
it("Open Error modal with default props", () => {
|
||||||
|
mount(Error, { props: {
|
||||||
|
subtitle: "Subtitle of Modal",
|
||||||
|
error: {
|
||||||
|
type: Failure.InsufficientBalance,
|
||||||
|
txHash: "0x8b678e51f970c5307bf45a8bcea373b597f9acbcea5c5ba784a1d383361a89d1",
|
||||||
|
message: "Not enough RAD"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, styles);
|
||||||
|
cy.get("[slot=title]").should("have.text", " Error");
|
||||||
|
cy.get("[slot=subtitle]").should("have.text", "Subtitle of Modal");
|
||||||
|
cy.get("[slot=body]").should("have.text", "Error: Not enough RAD");
|
||||||
|
cy.get("button").should("have.text", "Back");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Open Error modal with custom message", () => {
|
||||||
|
mount(Error, { props: {
|
||||||
|
subtitle: "Subtitle of Modal",
|
||||||
|
message: "Error message to check for",
|
||||||
|
} }, styles);
|
||||||
|
cy.get("[slot=subtitle]").should("have.text", "Subtitle of Modal");
|
||||||
|
cy.get("[slot=body]").should("have.text", "Error: Error message to check for");
|
||||||
|
cy.get("button").should("have.text", "Back");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Check floating modal changes button label to Close", () => {
|
||||||
|
mount(Error, { props: {
|
||||||
|
title: "Title of Modal",
|
||||||
|
subtitle: "Subtitle of Modal",
|
||||||
|
message: "Error message to check for",
|
||||||
|
floating: true
|
||||||
|
} }, styles);
|
||||||
|
cy.get("button").should("have.text", "Close");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import Search from "./Search.svelte";
|
||||||
|
import { mount } from "cypress-svelte-unit-test";
|
||||||
|
import { styles } from "@test/support/index";
|
||||||
|
|
||||||
|
describe('Search', function () {
|
||||||
|
it("Renders correctly", () => {
|
||||||
|
mount(Search, {}, styles);
|
||||||
|
cy.get("input").should("exist");
|
||||||
|
cy.get("input").should("have.attr", "placeholder", "Search a name or address...");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Search input displays and navigates correctly", () => {
|
||||||
|
mount(Search, {}, styles);
|
||||||
|
cy.get("input").type("cloudhead{enter}");
|
||||||
|
cy.get("input").should("have.value", "cloudhead");
|
||||||
|
cy.url().should("contain", "/resolver/query?q=cloudhead");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import SeedAddress from "./SeedAddress.svelte";
|
||||||
|
import { mount } from "cypress-svelte-unit-test";
|
||||||
|
import { styles } from "@test/support/index";
|
||||||
|
|
||||||
|
describe('SeedAddress', function () {
|
||||||
|
it("Renders correctly", () => {
|
||||||
|
mount(SeedAddress, {
|
||||||
|
props: {
|
||||||
|
id: "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
|
||||||
|
host: "seed.cloudhead.io",
|
||||||
|
port: 8776
|
||||||
|
}
|
||||||
|
}, styles);
|
||||||
|
cy.findByText("hydkkk…coygh1@seed.cloudhead.io").should("exist").should("have.attr", "href", "/seeds/seed.cloudhead.io");
|
||||||
|
cy.findByText(":8776").should("exist");
|
||||||
|
cy.findByText("Copy").should("exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Copies seed address correctly", () => {
|
||||||
|
cy.findByText("Copy").click().should("exist");
|
||||||
|
cy.findByText("Copy ✓").should("have.attr", "disabled");
|
||||||
|
// We invoke the cy.window here since doing the clipboard action in the runner window, would throw an error
|
||||||
|
cy.window().its("navigator.clipboard").invoke("readText").should("equal", "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1@seed.cloudhead.io:8776");
|
||||||
|
cy.wait(4000);
|
||||||
|
cy.findByText("Copy").click().should("exist").should("not.have.attr", "disabled");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Config } from "./config";
|
|
||||||
import { formatSeedAddress, formatSeedId, toClipboard } from "./utils";
|
import { formatSeedAddress, formatSeedId, toClipboard } from "./utils";
|
||||||
|
|
||||||
export let config: Config;
|
|
||||||
export let id: string;
|
export let id: string;
|
||||||
export let host: string;
|
export let host: string;
|
||||||
export let port: number;
|
export let port: number;
|
||||||
@@ -10,7 +8,7 @@
|
|||||||
let seedCopied = false;
|
let seedCopied = false;
|
||||||
|
|
||||||
const copySeed = (seedId: string, seedHost: string) => {
|
const copySeed = (seedId: string, seedHost: string) => {
|
||||||
return () => toClipboard(formatSeedAddress(seedId, seedHost, config)).then(() => {
|
return () => toClipboard(formatSeedAddress(seedId, seedHost, port)).then(() => {
|
||||||
seedCopied = true;
|
seedCopied = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
seedCopied = false;
|
seedCopied = false;
|
||||||
|
|||||||
@@ -251,7 +251,7 @@
|
|||||||
<!-- Seed Address -->
|
<!-- Seed Address -->
|
||||||
{#if profile.seedId && profile.seedHost}
|
{#if profile.seedId && profile.seedHost}
|
||||||
<div class="label">Seed</div>
|
<div class="label">Seed</div>
|
||||||
<SeedAddress {config} id={profile.seedId} host={profile.seedHost} port={config.seed.link.port} />
|
<SeedAddress id={profile.seedId} host={profile.seedHost} port={config.seed.link.port} />
|
||||||
{/if}
|
{/if}
|
||||||
<!-- Name/Profile -->
|
<!-- Name/Profile -->
|
||||||
<div class="label">Profile</div>
|
<div class="label">Profile</div>
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import BranchSelector from "./BranchSelector.svelte";
|
||||||
|
import { mount } from "cypress-svelte-unit-test";
|
||||||
|
import { styles } from "@test/support/index";
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
project: {
|
||||||
|
head: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||||
|
meta: {
|
||||||
|
name: "nakamoto",
|
||||||
|
description: "Privacy-preserving Bitcoin light-client implementation in Rust",
|
||||||
|
defaultBranch: "master",
|
||||||
|
maintainers: [
|
||||||
|
"rad:git:hnrkqdpm9ub19oc8dccx44echy76hzfsezyio"
|
||||||
|
],
|
||||||
|
delegates: [
|
||||||
|
"hyn9diwfnytahjq8u3iw63h9jte1ydcatxax3saymwdxqu1zo645pe"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
branches: [
|
||||||
|
["master", "e678629cd37c770c640a2cd997fc76303c815772"]
|
||||||
|
],
|
||||||
|
revision: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('BranchSelector', function () {
|
||||||
|
it("Render with commit = head and branch listing", () => {
|
||||||
|
mount(BranchSelector, {
|
||||||
|
props: defaultProps
|
||||||
|
}, styles
|
||||||
|
);
|
||||||
|
cy.findByText("master").should("exist");
|
||||||
|
cy.get("div.commit > div.hash.desktop").should("have.text", "e678629");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Test Branch selection", () => {
|
||||||
|
// The viewport here is to simulate desktop behaviour
|
||||||
|
cy.viewport(800, 300);
|
||||||
|
mount(BranchSelector, {
|
||||||
|
props: {
|
||||||
|
...defaultProps, branches: [
|
||||||
|
["master", "e678629cd37c770c640a2cd997fc76303c815772"],
|
||||||
|
["feature-branch", "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5"],
|
||||||
|
["xyz", "debf82ef3623ec11751a993bda85bac2ff1c6f00"],
|
||||||
|
],
|
||||||
|
branchesDropdown: true,
|
||||||
|
}, callbacks: {
|
||||||
|
revisionChanged: cy.stub().as("revisionChanged")
|
||||||
|
}
|
||||||
|
}, styles
|
||||||
|
);
|
||||||
|
cy.get("div.dropdown > div").first().click();
|
||||||
|
cy.get("@revisionChanged")
|
||||||
|
.should("be.calledOnce")
|
||||||
|
.its("firstCall.args.0.detail")
|
||||||
|
.should("equal", "feature-branch");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render with commit != head, passing a branch as rev and branch listing", () => {
|
||||||
|
mount(BranchSelector, {
|
||||||
|
props: {
|
||||||
|
...defaultProps, branches: [
|
||||||
|
["master", "e678629cd37c770c640a2cd997fc76303c815772"],
|
||||||
|
["feature-branch", "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5"],
|
||||||
|
["xyz", "debf82ef3623ec11751a993bda85bac2ff1c6f00"],
|
||||||
|
["xyz", "8b18e8006849e36872cb928d87179e4e222fb689"]
|
||||||
|
],
|
||||||
|
revision: "feature-branch"
|
||||||
|
}
|
||||||
|
}, styles
|
||||||
|
);
|
||||||
|
cy.findByText("feature-branch").should("exist");
|
||||||
|
cy.get("div.commit > div.hash.desktop").should("have.text", "29e8b7b");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render with commit != head passing a commit as rev and branch listing", () => {
|
||||||
|
mount(BranchSelector, {
|
||||||
|
props: {
|
||||||
|
...defaultProps,
|
||||||
|
revision: "debf82ef3623ec11751a993bda85bac2ff1c6f00"
|
||||||
|
}
|
||||||
|
}, styles
|
||||||
|
);
|
||||||
|
cy.get("div.commit > div.hash.desktop").should("have.text", "debf82ef3623ec11751a993bda85bac2ff1c6f00");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render with commit = head, without branch listing", () => {
|
||||||
|
mount(BranchSelector, {
|
||||||
|
props: {
|
||||||
|
...defaultProps,
|
||||||
|
revision: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||||
|
branches: [],
|
||||||
|
}
|
||||||
|
}, styles
|
||||||
|
);
|
||||||
|
cy.findByText("master").should("exist");
|
||||||
|
cy.get("div.commit > div.hash").should("have.text", "e678629");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Render without branch listing, commit != head", () => {
|
||||||
|
mount(BranchSelector, {
|
||||||
|
props: {
|
||||||
|
...defaultProps,
|
||||||
|
revision: "6b84e519d3c535879eb2b9ee8457bb70ca275a75",
|
||||||
|
branches: [],
|
||||||
|
}
|
||||||
|
}, styles
|
||||||
|
);
|
||||||
|
cy.get("div.commit > div.hash.desktop").should("have.text", "6b84e519d3c535879eb2b9ee8457bb70ca275a75");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
<!-- Seed Address -->
|
<!-- Seed Address -->
|
||||||
<div class="label">Address</div>
|
<div class="label">Address</div>
|
||||||
{#if info.version === "0.2.0" && info.host}
|
{#if info.version === "0.2.0" && info.host}
|
||||||
<SeedAddress {config} id={info.id} host={info.host} port={config.seed.link.port} />
|
<SeedAddress id={info.id} host={info.host} port={config.seed.link.port} />
|
||||||
{:else}
|
{:else}
|
||||||
<div class="seed-address subtle">N/A</div>
|
<div class="seed-address subtle">N/A</div>
|
||||||
<div class="desktop" />
|
<div class="desktop" />
|
||||||
|
|||||||
+85
-85
@@ -161,96 +161,96 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<!-- Address -->
|
<!-- Address -->
|
||||||
<div class="label">Address</div>
|
<div class="label">Address</div>
|
||||||
<div class="desktop"><Address {config} address={profile.address} /></div>
|
<div class="desktop"><Address {config} address={profile.address} /></div>
|
||||||
<div class="mobile"><Address compact {config} address={profile.address} /></div>
|
<div class="mobile"><Address compact {config} address={profile.address} /></div>
|
||||||
|
<div class="desktop" />
|
||||||
|
<!-- Project anchors -->
|
||||||
|
{#if profile.anchorsAccount}
|
||||||
|
<div class="label">Anchors</div>
|
||||||
|
<div class="desktop"><Address {config} address={profile.anchorsAccount} /></div>
|
||||||
|
<div class="mobile"><Address compact {config} address={profile.anchorsAccount} /></div>
|
||||||
<div class="desktop" />
|
<div class="desktop" />
|
||||||
<!-- Project anchors -->
|
{/if}
|
||||||
{#if profile.anchorsAccount}
|
<!-- Seed Address -->
|
||||||
<div class="label">Anchors</div>
|
{#if profile.seedId && profile.seedHost}
|
||||||
<div class="desktop"><Address {config} address={profile.anchorsAccount} /></div>
|
<div class="label">Seed</div>
|
||||||
<div class="mobile"><Address compact {config} address={profile.anchorsAccount} /></div>
|
<SeedAddress id={profile.seedId} host={profile.seedHost} port={config.seed.link.port} />
|
||||||
<div class="desktop" />
|
{/if}
|
||||||
|
<!-- Profile -->
|
||||||
|
<div class="label">Profile</div>
|
||||||
|
<div>
|
||||||
|
{#if profile.name}
|
||||||
|
<a href={profile.registry(config)} class="link">{profile.name}</a>
|
||||||
|
{:else}
|
||||||
|
<span class="subtle">Not set</span>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- Seed Address -->
|
|
||||||
{#if profile.seedId && profile.seedHost}
|
|
||||||
<div class="label">Seed</div>
|
|
||||||
<SeedAddress {config} id={profile.seedId} host={profile.seedHost} port={config.seed.link.port} />
|
|
||||||
{/if}
|
|
||||||
<!-- Profile -->
|
|
||||||
<div class="label">Profile</div>
|
|
||||||
<div>
|
|
||||||
{#if profile.name}
|
|
||||||
<a href={profile.registry(config)} class="link">{profile.name}</a>
|
|
||||||
{:else}
|
|
||||||
<span class="subtle">Not set</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<div class="desktop">
|
|
||||||
{#if isAuthorized(profile.address)}
|
|
||||||
<button class="tiny secondary" on:click={setName}>
|
|
||||||
Set
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{#await Org.getOrgsByMember(profile.address, config)}
|
<div class="desktop">
|
||||||
<Loading center />
|
{#if isAuthorized(profile.address)}
|
||||||
{:then orgs}
|
<button class="tiny secondary" on:click={setName}>
|
||||||
{#if orgs.length > 0}
|
Set
|
||||||
<div class="members">
|
</button>
|
||||||
{#each orgs as org}
|
|
||||||
<div class="member">
|
|
||||||
{#await Profile.get(org.address, ProfileType.Minimal, config)}
|
|
||||||
<Loading small margins />
|
|
||||||
{:then profile}
|
|
||||||
<div class="member-icon">
|
|
||||||
<Link to="/orgs/{profile.address}">
|
|
||||||
<Avatar source={profile.avatar ?? profile.address} address={profile.address} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div class="desktop">
|
|
||||||
<Address address={profile.address} compact
|
|
||||||
resolve noBadge noAvatar {profile} {config} />
|
|
||||||
</div>
|
|
||||||
{/await}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{:catch err}
|
</div>
|
||||||
<Message error>
|
</div>
|
||||||
<strong>Error: </strong> failed to load orgs: {err.message}.
|
{#await Org.getOrgsByMember(profile.address, config)}
|
||||||
</Message>
|
<Loading center />
|
||||||
{/await}
|
{:then orgs}
|
||||||
<div class="projects">
|
{#if orgs.length > 0}
|
||||||
{#if profile.anchorsAccount}
|
<div class="members">
|
||||||
{#await Org.get(profile.anchorsAccount, config)}
|
{#each orgs as org}
|
||||||
<Loading center fadeIn />
|
<div class="member">
|
||||||
{:then org}
|
{#await Profile.get(org.address, ProfileType.Minimal, config)}
|
||||||
{#if org}
|
<Loading small margins />
|
||||||
{#await org.getProjects(config) then projects}
|
{:then profile}
|
||||||
{#each projects as project}
|
<div class="member-icon">
|
||||||
<div class="project">
|
<Link to="/orgs/{profile.address}">
|
||||||
<Project {project} user={addressOrName} config={profile.config(config)}>
|
<Avatar source={profile.avatar ?? profile.address} address={profile.address} />
|
||||||
<span slot="stateHash">
|
</Link>
|
||||||
<span class="mobile">commit {formatCommit(project.anchor.stateHash)}</span>
|
</div>
|
||||||
<span class="desktop">commit {project.anchor.stateHash}</span>
|
<div class="desktop">
|
||||||
</span>
|
<Address address={profile.address} compact
|
||||||
</Project>
|
resolve noBadge noAvatar {profile} {config} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
|
||||||
{:catch err}
|
|
||||||
<Message error>
|
|
||||||
<strong>Error: </strong> failed to load projects: {err.message}.
|
|
||||||
</Message>
|
|
||||||
{/await}
|
{/await}
|
||||||
{/if}
|
</div>
|
||||||
{/await}
|
{/each}
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
|
{:catch err}
|
||||||
|
<Message error>
|
||||||
|
<strong>Error: </strong> failed to load orgs: {err.message}.
|
||||||
|
</Message>
|
||||||
|
{/await}
|
||||||
|
<div class="projects">
|
||||||
|
{#if profile.anchorsAccount}
|
||||||
|
{#await Org.get(profile.anchorsAccount, config)}
|
||||||
|
<Loading center fadeIn />
|
||||||
|
{:then org}
|
||||||
|
{#if org}
|
||||||
|
{#await org.getProjects(config) then projects}
|
||||||
|
{#each projects as project}
|
||||||
|
<div class="project">
|
||||||
|
<Project {project} user={addressOrName} config={profile.config(config)}>
|
||||||
|
<span slot="stateHash">
|
||||||
|
<span class="mobile">commit {formatCommit(project.anchor.stateHash)}</span>
|
||||||
|
<span class="desktop">commit {project.anchor.stateHash}</span>
|
||||||
|
</span>
|
||||||
|
</Project>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{:catch err}
|
||||||
|
<Message error>
|
||||||
|
<strong>Error: </strong> failed to load projects: {err.message}.
|
||||||
|
</Message>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
<svelte:component this={setNameForm} entity={new User(profile.address)} {config} on:close={() => setNameForm = null} />
|
<svelte:component this={setNameForm} entity={new User(profile.address)} {config} on:close={() => setNameForm = null} />
|
||||||
{:catch err}
|
{:catch err}
|
||||||
|
|||||||
+8
-7
@@ -72,8 +72,8 @@ export function isAddressEqual(left: string, right: string): boolean {
|
|||||||
return left.toLowerCase() === right.toLowerCase();
|
return left.toLowerCase() === right.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatSeedAddress(id: string, host: string, config: Config): string {
|
export function formatSeedAddress(id: string, host: string, port: number): string {
|
||||||
return `${id}@${host}:${config.seed.link.port}`;
|
return `${id}@${host}:${port}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatSeedId(id: string): string {
|
export function formatSeedId(id: string): string {
|
||||||
@@ -111,13 +111,14 @@ export function formatIpfsFile(ipfs: string | undefined): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function formatHash(hash: string): string {
|
export function formatHash(hash: string): string {
|
||||||
|
if (hash.length < 10) return hash;
|
||||||
return hash.substring(0, 6)
|
return hash.substring(0, 6)
|
||||||
+ '...'
|
+ '...'
|
||||||
+ hash.substring(hash.length - 4, hash.length);
|
+ hash.substring(hash.length - 4, hash.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatCommit(oid: string): string {
|
export function formatCommit(oid: string): string {
|
||||||
return oid.substring(0,7);
|
return oid.substring(0, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatOrg(input: string, config: Config): string {
|
export function formatOrg(input: string, config: Config): string {
|
||||||
@@ -130,7 +131,7 @@ export function formatOrg(input: string, config: Config): string {
|
|||||||
|
|
||||||
export function capitalize(s: string): string {
|
export function capitalize(s: string): string {
|
||||||
if (s === "") return s;
|
if (s === "") return s;
|
||||||
return s[0].toUpperCase() + s.substr(1);
|
return s[0].toUpperCase() + s.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a domain name, eg. 'cloudhead.radicle.eth' and
|
// Takes a domain name, eg. 'cloudhead.radicle.eth' and
|
||||||
@@ -196,7 +197,7 @@ export function isENSName(input: string, config: Config): boolean {
|
|||||||
return regEx.test(input);
|
return regEx.test(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the input is an Ethereum address.
|
// Check whether the input is an checksummed or all lowercase Ethereum address.
|
||||||
export function isAddress(input: string): boolean {
|
export function isAddress(input: string): boolean {
|
||||||
return ethers.utils.isAddress(input);
|
return ethers.utils.isAddress(input);
|
||||||
}
|
}
|
||||||
@@ -424,7 +425,7 @@ export function isMarkdownPath(path: string): boolean {
|
|||||||
// Also accepts in dev env 0.0.0.0 as domain
|
// Also accepts in dev env 0.0.0.0 as domain
|
||||||
export function isDomain(input: string): boolean {
|
export function isDomain(input: string): boolean {
|
||||||
return (/^[a-z][a-z0-9.-]+$/.test(input) && /\.[a-z]+$/.test(input))
|
return (/^[a-z][a-z0-9.-]+$/.test(input) && /\.[a-z]+$/.test(input))
|
||||||
|| (!import.meta.env.PROD && /^0.0.0.0$/.test(input)) ;
|
|| (! import.meta.env.PROD && /^0.0.0.0$/.test(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propose a Gnosis Safe multi-sig transaction.
|
// Propose a Gnosis Safe multi-sig transaction.
|
||||||
@@ -504,7 +505,7 @@ export async function executeSignedSafeTransaction(
|
|||||||
...signedTx,
|
...signedTx,
|
||||||
gasPrice: Number(signedTx.gasPrice),
|
gasPrice: Number(signedTx.gasPrice),
|
||||||
data: signedTx.data
|
data: signedTx.data
|
||||||
} );
|
});
|
||||||
|
|
||||||
signedTx.confirmations.forEach(confirmation => {
|
signedTx.confirmations.forEach(confirmation => {
|
||||||
const signature = new EthSignSignature(confirmation.owner, confirmation.signature);
|
const signature = new EthSignSignature(confirmation.owner, confirmation.signature);
|
||||||
|
|||||||
+7
-3
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src",
|
||||||
|
"cypress"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules/*"
|
"node_modules/*"
|
||||||
@@ -11,7 +12,9 @@
|
|||||||
"module": "es2020",
|
"module": "es2020",
|
||||||
"types": [
|
"types": [
|
||||||
"svelte",
|
"svelte",
|
||||||
"vite/client"
|
"vite/client",
|
||||||
|
"cypress",
|
||||||
|
"@testing-library/cypress"
|
||||||
],
|
],
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
@@ -22,7 +25,8 @@
|
|||||||
"importsNotUsedAsValues": "error",
|
"importsNotUsedAsValues": "error",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@app/*": ["./src/*"]
|
"@app/*": ["./src/*"],
|
||||||
|
"@test/*": ["./cypress/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"noEmit": true
|
"noEmit": true
|
||||||
|
|||||||
+15
-2
@@ -1,18 +1,31 @@
|
|||||||
|
/// <reference types="vitest" />
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { UserConfig } from 'vite';
|
import { UserConfig } from 'vite';
|
||||||
import svelte from '@sveltejs/vite-plugin-svelte';
|
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||||
import rewriteAll from 'vite-plugin-rewrite-all';
|
import rewriteAll from 'vite-plugin-rewrite-all';
|
||||||
|
import IstanbulPlugin from 'vite-plugin-istanbul';
|
||||||
|
|
||||||
|
|
||||||
const config: UserConfig = {
|
const config: UserConfig = {
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: ['svelte-routing', '@pedrouid/environment', '@pedrouid/iso-crypto']
|
exclude: ['svelte-routing', '@pedrouid/environment', '@pedrouid/iso-crypto']
|
||||||
},
|
},
|
||||||
plugins: [svelte(), rewriteAll()],
|
plugins: [
|
||||||
|
svelte({ hot: !process.env.VITEST }),
|
||||||
|
rewriteAll(),
|
||||||
|
IstanbulPlugin({
|
||||||
|
include: "src/**/*",
|
||||||
|
exclude: ["node_modules"],
|
||||||
|
extension: [".ts", ".svelte"],
|
||||||
|
cypress: true
|
||||||
|
})
|
||||||
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
// This is needed for vite not to choke.
|
// This is needed for vite not to choke.
|
||||||
"caip": path.resolve("./node_modules/caip/dist/umd/index.min.js"),
|
"caip": path.resolve("./node_modules/caip/dist/umd/index.min.js"),
|
||||||
'@app': path.resolve('./src'),
|
'@app': path.resolve('./src'),
|
||||||
|
'@test': path.resolve('./cypress'),
|
||||||
// Polyfill for Node.js 'stream' library.
|
// Polyfill for Node.js 'stream' library.
|
||||||
'stream': path.resolve('./src/polyfills/stream.ts'),
|
'stream': path.resolve('./src/polyfills/stream.ts'),
|
||||||
'typedarray-to-buffer': path.resolve('./src/polyfills/typedarray-to-buffer.js'),
|
'typedarray-to-buffer': path.resolve('./src/polyfills/typedarray-to-buffer.js'),
|
||||||
|
|||||||
Reference in New Issue
Block a user