Improve testing suite

Signed-off-by: Sebastian Martinez <sebastinez@me.com>
This commit is contained in:
Sebastian Martinez
2022-03-24 15:05:15 +01:00
parent 6f2f8c454f
commit 286cb1cd21
44 changed files with 2550 additions and 482 deletions
@@ -0,0 +1,18 @@
name: check-integration-test
on: [push, pull_request]
jobs:
cypress-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v2
with:
browser: chrome
start: npm start
wait-on: 'http://localhost:3000'
command: npm run test:e2e
@@ -1,4 +1,4 @@
name: check-components
name: check-unit-test
on: [push, pull_request]
jobs:
@@ -10,5 +10,5 @@ jobs:
with:
node-version: '14'
- run: npm ci
- run: ./scripts/test
- run: ./scripts/unit-test
shell: bash
+3
View File
@@ -5,3 +5,6 @@ NOTES
# Coverage
.nyc_output
coverage
# Integration Tests
cypress/screenshots
+6
View File
@@ -0,0 +1,6 @@
{
"heads": {
"master": "cbf5df499ab4f4a908f1756fbe2c236a4530516a",
"main": "56e4e029c294b08546386e1fb706b772c7433c49"
}
}
+115
View File
@@ -0,0 +1,115 @@
{
"header": {
"sha1": "cbf5df499ab4f4a908f1756fbe2c236a4530516a",
"author": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"summary": "initial commit",
"description": "this is the first commit of many",
"committer": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"committerTime": 1646236685
},
"stats": {
"additions": 0,
"deletions": 0
},
"diff": {
"created": [
"README.md"
],
"deleted": [
"LICENSE"
],
"moved": [],
"copied": [],
"modified": [
{
"path": "foo.bar",
"diff": {
"type": "plain",
"hunks": [
{
"header": "@@ -13,7 +13,11 @@ If executed, this proposal will:\n",
"lines": [
{
"type": "context",
"line": "\n",
"lineNumOld": 13,
"lineNumNew": 13
},
{
"type": "context",
"line": "After execution, the Timelock holds all Uniswap LP tokens for the RAD/USDC pair.\n",
"lineNumOld": 14,
"lineNumNew": 14
},
{
"type": "context",
"line": "\n",
"lineNumOld": 15,
"lineNumNew": 15
},
{
"type": "deletion",
"line": "To prevent front-running, the RAD/USDC balances are set through the Uniswap router *proxy* contract, deployed at `0xB76FC4EbE4fC0CC34AF440Ad79565A68Bfcb095e`. Only the Radicle Foundation can set these balances, via the `setLiquidity` function. This contract function must be called as close as possible to the execution of this proposal, to provide liquidity at the correct market price.\n",
"lineNum": 16
},
{
"type": "addition",
"line": "To prevent front-running, the RAD/USDC balances are set through the Uniswap\n",
"lineNum": 16
},
{
"type": "addition",
"line": "router *proxy* contract, deployed at `0xB76FC4EbE4fC0CC34AF440Ad79565A68Bfcb095e`.\n",
"lineNum": 17
},
{
"type": "addition",
"line": "Only the Radicle Foundation can set these balances, via the `setLiquidity`\n",
"lineNum": 18
},
{
"type": "addition",
"line": "function. This contract function must be called as close as possible to the\n",
"lineNum": 19
},
{
"type": "addition",
"line": "execution of this proposal, to provide liquidity at the correct market price.\n",
"lineNum": 20
},
{
"type": "context",
"line": "\n",
"lineNumOld": 17,
"lineNumNew": 21
},
{
"type": "context",
"line": "## Notes\n",
"lineNumOld": 18,
"lineNumNew": 22
},
{
"type": "context",
"line": "\n",
"lineNumOld": 19,
"lineNumNew": 23
}
]
}
]
}
}
]
},
"branches": [
"main",
"master"
]
}
+23
View File
@@ -0,0 +1,23 @@
{
"headers": [
{
"sha1": "cbf5df499ab4f4a908f1756fbe2c236a4530516a",
"author": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"summary": "initial commit",
"description": "",
"committer": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"committerTime": 1646236685
}
],
"stats": {
"commits": 1,
"branches": 1,
"contributors": 1
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"message": "Welcome!",
"service": "radicle-http-api",
"version": "0.2.0",
"path": "/",
"links": [
{
"href": "/v1/projects",
"rel": "projects",
"type": "GET"
},
{
"href": "/v1/peer",
"rel": "peer",
"type": "GET"
},
{
"href": "/v1/delegates/:urn/projects",
"rel": "projects",
"type": "GET"
}
]
}
+16
View File
@@ -0,0 +1,16 @@
{
"urn": "rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy",
"name": "bright-forest-protocol",
"description": "bfc-sc",
"defaultBranch": "main",
"delegates": [
{
"type": "indirect",
"urn": "rad:git:hnrkqz68g6nddigodpgjmc1u8ydpzb8fqq8ro",
"ids": [
"hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke"
]
}
],
"head": "56e4e029c294b08546386e1fb706b772c7433c49"
}
+16
View File
@@ -0,0 +1,16 @@
[{
"urn": "rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy",
"name": "mocked-seed-protocol",
"description": "bfc-sc",
"defaultBranch": "main",
"delegates": [
{
"type": "indirect",
"urn": "rad:git:hnrkqz68g6nddigodpgjmc1u8ydpzb8fqq8ro",
"ids": [
"hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke"
]
}
],
"head": "56e4e029c294b08546386e1fb706b772c7433c49"
}]
+1
View File
@@ -0,0 +1 @@
{ "id": "hyy841u4phudmr8s5rg1jjwd1ct7x7438wmjwtsm464y8uyxyhyi6c" }
+24
View File
@@ -0,0 +1,24 @@
{
"binary": false,
"html": false,
"content": "# Basic Sample Hardhat Project\n\nThis project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, a sample script that deploys that contract, and an example of a task implementation, which simply lists the available accounts.\n\nTry running some of the following tasks:\n\n```shell\nnpx hardhat accounts\nnpx hardhat compile\nnpx hardhat clean\nnpx hardhat test\nnpx hardhat node\nnode scripts/sample-script.js\nnpx hardhat help\n```\n",
"info": {
"name": "README.md",
"objectType": "BLOB",
"lastCommit": {
"sha1": "cbf5df499ab4f4a908f1756fbe2c236a4530516a",
"author": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"summary": "initial commit",
"description": "",
"committer": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"committerTime": 1646236685
}
},
"path": "README.md"
}
+7
View File
@@ -0,0 +1,7 @@
[
{
"id": "hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke",
"name": "dabit3",
"delegate": true
}
]
+100
View File
@@ -0,0 +1,100 @@
{
"path": "",
"entries": [
{
"path": "contracts",
"info": {
"name": "contracts",
"objectType": "TREE",
"lastCommit": null
}
},
{
"path": "scripts",
"info": {
"name": "scripts",
"objectType": "TREE",
"lastCommit": null
}
},
{
"path": "test",
"info": {
"name": "test",
"objectType": "TREE",
"lastCommit": null
}
},
{
"path": ".gitignore",
"info": {
"name": ".gitignore",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "README.md",
"info": {
"name": "README.md",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "hardhat.config.js",
"info": {
"name": "hardhat.config.js",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "package-lock.json",
"info": {
"name": "package-lock.json",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "package.json",
"info": {
"name": "package.json",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "yarn.lock",
"info": {
"name": "yarn.lock",
"objectType": "BLOB",
"lastCommit": null
}
}
],
"info": {
"name": "",
"objectType": "TREE",
"lastCommit": {
"sha1": "56e4e029c294b08546386e1fb706b772c7433c49",
"author": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"summary": "emit event",
"description": "",
"committer": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"committerTime": 1646237176
}
},
"stats": {
"commits": 3,
"branches": 1,
"contributors": 1
}
}
+100
View File
@@ -0,0 +1,100 @@
{
"path": "",
"entries": [
{
"path": "contracts",
"info": {
"name": "contracts",
"objectType": "TREE",
"lastCommit": null
}
},
{
"path": "scripts",
"info": {
"name": "scripts",
"objectType": "TREE",
"lastCommit": null
}
},
{
"path": "test",
"info": {
"name": "test",
"objectType": "TREE",
"lastCommit": null
}
},
{
"path": ".gitignore",
"info": {
"name": ".gitignore",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "README.md",
"info": {
"name": "README.md",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "hardhat.config.js",
"info": {
"name": "hardhat.config.js",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "package-lock.json",
"info": {
"name": "package-lock.json",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "package.json",
"info": {
"name": "package.json",
"objectType": "BLOB",
"lastCommit": null
}
},
{
"path": "yarn.lock",
"info": {
"name": "yarn.lock",
"objectType": "BLOB",
"lastCommit": null
}
}
],
"info": {
"name": "",
"objectType": "TREE",
"lastCommit": {
"sha1": "56e4e029c294b08546386e1fb706b772c7433c49",
"author": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"summary": "emit event",
"description": "",
"committer": {
"name": "dabit3",
"email": "dabit3@gmail.com"
},
"committerTime": 1646237176
}
},
"stats": {
"commits": 3,
"branches": 1,
"contributors": 1
}
}
+29
View File
@@ -0,0 +1,29 @@
/// <reference types="cypress" />
import { MockExtensionProvider } from "../support";
describe("Landing page", () => {
it("Loads user, orgs and seeds", () => {
cy.intercept("https://willow.radicle.garden:8777/", { fixture: "projectHome.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
cy.intercept("https://pine.radicle.garden:8777/", { fixture: "projectHome.json" });
cy.intercept("https://pine.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
cy.intercept("https://maple.radicle.garden:8777/", { fixture: "projectHome.json" });
cy.intercept("https://maple.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
cy.intercept("https://gateway.thegraph.com/api/1758a78ae257ad4906f9c638e4a68c19/subgraphs/id/0x2f0963e77ca6ac0c2dad1bf4147b6b40e0dd8728-0", {
"data": { "safe": null }
});
cy.intercept("https://maple.radicle.garden:8777/v1/projects", { fixture: "projectList.json" });
cy.intercept("https://pine.radicle.garden:8777/v1/projects", { fixture: "projectList.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects", { fixture: "projectList.json" });
cy.visit("/", {
onBeforeLoad(win) {
win.ethereum = new MockExtensionProvider();
},
});
cy.get("div.card-label")
.first()
.should("have.text", "willow.radicle.garden")
.next()
.should("have.text", "1 project(s)");
});
});
+115
View File
@@ -0,0 +1,115 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/// <reference types="cypress" />
import { MockExtensionProvider } from "../support";
describe("Project View", () => {
before(() => {
cy.intercept("https://willow.radicle.garden:8777/", { fixture: "projectHome.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/peer", { fixture: "projectPeer.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/bright-forest-protocol", { fixture: "projectInfo.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy", { fixture: "projectInfo.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes", { fixture: "projectRemotes.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/56e4e029c294b08546386e1fb706b772c7433c49", { fixture: "projectTree56e4e02.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/cbf5df499ab4f4a908f1756fbe2c236a4530516a", { fixture: "projectTreecbf5df4.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke", { fixture: "projectBranches.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/readme/56e4e029c294b08546386e1fb706b772c7433c49", { fixture: "projectReadme.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commits?parent=cbf5df499ab4f4a908f1756fbe2c236a4530516a", { fixture: "projectCommits.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/readme/cbf5df499ab4f4a908f1756fbe2c236a4530516a", { fixture: "projectReadme.json" });
cy.intercept("https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commits/cbf5df499ab4f4a908f1756fbe2c236a4530516a", { fixture: "projectCommit.json" });
});
it("Load project view and use peer and branch selector ", () => {
cy.visit("/seeds/willow.radicle.garden/bright-forest-protocol", {
onBeforeLoad(win) {
win.ethereum = new MockExtensionProvider();
},
});
cy.log("Check that the project view is loaded and populated with the stubbed data");
cy.log("Page Header");
cy.get("div.title").contains("bright-forest-protocol");
cy.get("div.urn").contains("rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy");
cy.get("div.description").contains("bfc-sc");
cy.log("README");
cy.get("div.column-right article div.markdown h1").should("have.text", "Basic Sample Hardhat Project");
cy.log("Project Header");
cy.get("div.stat.seed span").should("have.text", "willow.radicle.garden");
cy.get("div.stat.commit-count").should("have.text", "3 commit(s)");
cy.get("div.stat.contributor-count").should("have.text", "1 contributor(s)");
cy.get("div.anchor span.anchor-label").contains("not anchored 🔓");
cy.get("div.stat.branch").should("have.class", "not-allowed").should("have.text", "main");
cy.get("div.hash.desktop").should("have.text", "56e4e02");
cy.get("div.clone").click();
cy.log("Peer Selection");
cy.get("div.selector").click();
cy.get("div.dropdown-item").contains("dabit3 hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke delegate").click();
cy.location().should((location) => {
expect(location.pathname).to.eq('/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/tree');
});
cy.get("span.peer-id span[title='hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke']").should("have.text", "hyndc7…j9oeke");
cy.get("div.stat.peer span.peer-id").should("have.text", "dabit3");
cy.get("div.stat.peer span.badge").should("have.text", "delegate");
cy.log("Branch Selection");
cy.get("div.commit div.stat.branch").click();
cy.get("div.dropdown-item")
.first()
.contains("main")
.next()
.contains("master")
.click();
cy.location().should((location) => {
expect(location.pathname).to.eq('/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/tree/master');
});
cy.get("div.stat.branch").should("have.text", "master");
cy.get("div.hash.desktop").should("have.text", "cbf5df4");
cy.log("Commit history view");
cy.get("div.stat.commit-count")
.should("not.have.class", "active")
.click();
cy.location().should((location) => {
expect(location.pathname).to.eq('/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/history/master');
});
cy.get("div.stat.commit-count").should("have.class", "active");
cy.log("Commit Group");
cy.get("header.commit-date").should("have.text", "Wednesday, March 2, 2022");
cy.log("Commit Trailer");
cy.get("div.summary span.hash")
.should("have.text", "cbf5df4")
.next()
.should("have.text", "initial commit");
cy.get("div.commit div.right span")
.first()
.should("have.text", "dabit3")
.next()
.should("have.text", "Wed, 02 Mar 2022 15:58:05 GMT");
cy.get("div.summary").click();
cy.log("Commit Detail View");
cy.location().should((location) => {
expect(location.pathname).to.eq('/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/commits/cbf5df499ab4f4a908f1756fbe2c236a4530516a');
});
cy.get("header div.summary h3").should("have.text", "initial commit");
cy.get("header pre.description").should("have.text", "this is the first commit of many");
cy.get("header div.meta")
.first()
.should("have.text", "Committed by dabit3 <dabit3@gmail.com> Wed, 02 Mar 2022 15:58:05 GMT")
.next()
.should("have.text", "Authored by dabit3 <dabit3@gmail.com>");
cy.get("div.changeset-summary").should("have.text", "1 file(s) changed\n with\n 0 addition(s)\n and\n 0 deletion(s)");
cy.get("header.file-header:nth-child(1) div.file-data > *")
.first()
.should("have.text", "README.md")
.next()
.should("have.text", "created");
cy.get("tr.diff-line td.diff-line-number").contains("16");
cy.get("tr.diff-line td.diff-line-content").contains("To prevent front-running, the RAD/USDC balances are set through the Uniswap router *proxy* contract");
});
});
+45 -15
View File
@@ -1,19 +1,49 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import '@cypress/code-coverage/support';
import '@testing-library/cypress/add-commands';
import { ethers } from 'ethers';
export const styles = {
cssFile: "public/index.css",
style: `
.wrapper {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
declare global {
interface Window {
ethereum: any;
}
`,
html: `
<div class="wrapper">
<div id="here" />
</div>
`
};
}
export class MockExtensionProvider extends ethers.providers.BaseProvider {
isMetaMask = true;
constructor() {
super({ name: "homestead", chainId: 1 });
console.log("Creating Mock provider");
}
async request({ method, params }: { method: string; params: any }): Promise<any> {
switch (method) {
case 'eth_chainId':
return "1";
case 'net_version':
return "1";
case 'eth_call':
return resolveEthCall(params);
case 'eth_getCode':
return "0x";
default:
console.log("Unknown method", method);
break;
}
}
}
function resolveEthCall(params: { to: string; data: string }[]): string {
// Get Resolver
if (params[0].to === "0x00000000000c2e074ec69a0dfb2997ba6c7d2e1e") {
return "0x0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41";
// Get ENS Attributes
} else if (params[0].to === "0x4976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41" && params[0].data === "0x59d1d43c567c364804de7bbedb53f583e483f6b73513fd2f44299e281024e4719da0b332000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066176617461720000000000000000000000000000000000000000000000000000") {
return "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001f68747470733a2f2f636c6f7564686561642e696f2f6176617461722e706e6700";
// Get Org informations
} else if (params[0].to === "0x8152237402e0f194176154c3a6ea1eb99b611482" && params[0].data === "0x8da5cb5b") {
return "0x000000000000000000000000ceab094641905c209cc796fc8037dd9ecc87ca2f";
} else {
return "0x000000000000000000000000394b920c5d39e0ca40fca2871569b6b90d750c7c";
}
}
+1255 -264
View File
File diff suppressed because it is too large Load Diff
+10 -5
View File
@@ -5,7 +5,10 @@
"serve": "vite preview",
"build": "scripts/build",
"check": "scripts/check",
"test": "cypress run-ct"
"coverage": "npx nyc report",
"test:unit": "vitest run",
"test:components": "cypress run-ct",
"test:e2e": "cypress run"
},
"type": "module",
"engines": {
@@ -16,20 +19,22 @@
"@cypress/vite-dev-server": "^2.2.2",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.36",
"@testing-library/cypress": "^8.0.2",
"@testing-library/svelte": "^3.1.0",
"@tsconfig/svelte": "^1.0.13",
"@types/sanitize-html": "^2.6.2",
"@typescript-eslint/eslint-plugin": "^4.26.1",
"@typescript-eslint/parser": "^4.26.1",
"cypress": "^9.3.1",
"cypress": "^9.5.1",
"eslint": "^7.28.0",
"eslint-plugin-radicle": "^0.2.0",
"eslint-plugin-svelte3": "^3.2.0",
"radicle-svelte-unit-test": "^1.0.0",
"happy-dom": "^2.50.0",
"svelte-check": "^2.4.1",
"typescript": "^4.2.4",
"vite": "^2.7.13",
"vite-plugin-istanbul": "^2.4.0",
"vite-plugin-rewrite-all": "^0.1.2"
"vite-plugin-istanbul": "^2.5.1",
"vite-plugin-rewrite-all": "^0.1.2",
"vitest": "^0.6.3"
},
"dependencies": {
"@datamodels/identity-profile-basic": "^0.1.2",
+1
View File
@@ -1,4 +1,5 @@
#!/bin/sh
set -e
vite build
cp _redirects build/_redirects
+1
View File
@@ -1,3 +1,4 @@
#!/bin/sh
set -e
npx tsc --noEmit && npx svelte-check
+1
View File
@@ -1,4 +1,5 @@
#!/bin/sh
set -e
# Checks local /node_modules,
# if not found will download that package and execute it.
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
npm test && npx nyc report
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
set -e
npm run test:unit
npm run test:components
+46
View File
@@ -0,0 +1,46 @@
import BlockTimer from "./BlockTimer.svelte";
import { render } from "@testing-library/svelte";
import "@public/index.css";
import type { EventType, Listener } from "@ethersproject/abstract-provider";
describe('BlockTimer', function () {
it("increases correctly the loading bar", () => {
let block = 1;
const props = {
config: {
provider: {
on: (event: EventType, listener: Listener) => {
if (event === "block") {
listener(block);
}
}
}
},
startBlock: 1,
duration: 3
};
const { rerender } = render(BlockTimer, props);
cy.get("div.loader").should("have.attr", "style", "width: 0%;").then(() => {
block += 1;
rerender(props);
});
cy.get("div.loader").last().should("have.attr", "style", "width: 33%;").then(() => {
block += 1;
rerender(props);
});
cy.get("div.loader").last().should("have.attr", "style", "width: 66%;").then(() => {
block += 1;
rerender(props);
});
cy.get("div.loader").last().should("have.attr", "style", "width: 99%;").then(() => {
block += 1;
rerender(props);
});
});
});
+1 -1
View File
@@ -29,5 +29,5 @@
</style>
<div class="parent">
<div class="loader" style="width: {(currentBlock - startBlock) * 10}%" />
<div class="loader" style="width: {(currentBlock - startBlock) * Math.floor(100 / duration)}%" />
</div>
+17 -19
View File
@@ -1,11 +1,11 @@
import Error from "./Error.svelte";
import { mount } from "radicle-svelte-unit-test";
import { render } from "@testing-library/svelte";
import { Failure } from '@app/error';
import { styles } from "@test/support/index";
import "@public/index.css";
describe('Error', function () {
it("Open Error modal with default props", () => {
mount(Error, { props: {
it("should show passed in props", () => {
render(Error, { props: {
subtitle: "Subtitle of Modal",
error: {
type: Failure.InsufficientBalance,
@@ -13,30 +13,28 @@ describe('Error', function () {
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");
});
cy.findByText("Error");
cy.findByText("Subtitle of Modal");
cy.findByText("Not enough RAD");
cy.findByText("Back");
});
it("Open Error modal with custom message", () => {
mount(Error, { props: {
it("should show custom error message", () => {
render(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");
} });
cy.findByText("Error message to check for");
});
it("Check floating modal changes button label to Close", () => {
mount(Error, { props: {
it("should change button label to Close when floating", () => {
render(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");
} });
cy.findByText("Close");
});
});
+17
View File
@@ -0,0 +1,17 @@
import NotFound from "./NotFound.svelte";
import { render } from "@testing-library/svelte";
import "@public/index.css";
describe('NotFound', function () {
it("shows passed props correctly", () => {
render(NotFound, {
props: {
title: "nakamoto",
subtitle: "Sorry, the requested project was not found."
}
});
cy.findByText("nakamoto");
cy.findByText("Sorry, the requested project was not found.");
cy.findByText("Back");
});
});
+23 -11
View File
@@ -1,18 +1,30 @@
import Search from "./Search.svelte";
import { mount } from "radicle-svelte-unit-test";
import { styles } from "@test/support/index";
import { fireEvent, render } from "@testing-library/svelte";
import "@public/index.css";
describe('Search', function () {
it("Renders correctly", () => {
mount(Search, {}, styles);
cy.get("input").should("exist");
describe('Logic', () => {
it("show a appropiate placeholder", () => {
render(Search);
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");
it("allow input a query and navigates accordingly", () => {
render(Search);
cy.get("input").type("cloudhead.radicle.eth{enter}");
cy.get("input").should("have.value", "cloudhead.radicle.eth");
cy.url().should("contain", "/resolver/query?q=cloudhead.radicle.eth");
});
});
describe("Events", () => {
it("should fire an event when the input changes", () => {
const { component } = render(Search);
const mock = cy.spy();
component.$on("search", mock);
cy.get("input").then(([input]) => {
fireEvent.keyDown(input, { key: "Enter" });
expect(mock).to.have.been.calledOnce;
});
});
});
+25 -6
View File
@@ -1,18 +1,37 @@
import SeedAddress from "./SeedAddress.svelte";
import { mount } from "radicle-svelte-unit-test";
import { styles } from "@test/support/index";
import { render } from "@testing-library/svelte";
import "@public/index.css";
describe('SeedAddress', function () {
it("Renders correctly", () => {
mount(SeedAddress, {
it("shows the seed emoji and seed host", () => {
render(SeedAddress, {
props: {
seed: {
id: "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
emoji: "🐱",
host: "seed.cloudhead.io",
},
port: 8776
}
}, styles);
cy.findByText("seed.cloudhead.io").should("exist").should("have.attr", "href", "/seeds/seed.cloudhead.io");
});
cy.get("span.seed-icon").should("have.text", "🐱");
cy.findByText("seed.cloudhead.io").should("have.attr", "href", "/seeds/seed.cloudhead.io").should("be.visible");
});
it("shows the full seed id", () => {
render(SeedAddress, {
props: {
seed: {
id: "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
emoji: "🐱",
host: "seed.cloudhead.io",
},
port: 8776,
full: true
}
});
cy.get("span.seed-icon").should("have.text", "🐱");
cy.findByText("hydkkk…coygh1@seed.cloudhead.io").should("be.visible");
cy.findByText(":8776").should("be.visible");
});
});
+110 -46
View File
@@ -1,6 +1,6 @@
import BranchSelector from "./BranchSelector.svelte";
import { mount } from "radicle-svelte-unit-test";
import { styles } from "@test/support/index";
import { fireEvent, render } from "@testing-library/svelte";
import "@public/index.css";
const defaultProps = {
project: {
@@ -18,41 +18,63 @@ const defaultProps = {
},
branches: { "master": "e678629cd37c770c640a2cd997fc76303c815772" },
revision: "e678629cd37c770c640a2cd997fc76303c815772",
toggleDropdown: () => "branch"
};
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");
describe('Logic', () => {
it("should show defaultBranch label and head commit if revision == head", () => {
const { rerender } = render(BranchSelector, {
props: defaultProps,
});
cy.get("div.stat.branch").should("be.visible").should("have.text", "master");
cy.get("div.hash.mobile").should("be.visible").should("have.text", "e678629");
// If project.head is null we should get the head from branches.
rerender({
props: {
...defaultProps,
project: {
head: null,
urn: "rad:git:hnrkqdpm9ub19oc8dccx44echy76hzfsezyio",
name: "nakamoto",
description: "Privacy-preserving Bitcoin light-client implementation in Rust",
defaultBranch: "master",
maintainers: [
"rad:git:hnrkqdpm9ub19oc8dccx44echy76hzfsezyio"
],
delegates: [
"hyn9diwfnytahjq8u3iw63h9jte1ydcatxax3saymwdxqu1zo645pe"
]
}
}
});
cy.get("div.stat.branch").should("be.visible").should("have.text", "master");
cy.get("div.hash.mobile").should("be.visible").should("have.text", "e678629");
});
it("Test Branch selection", () => {
// The viewport here is to simulate desktop behaviour
cy.viewport(800, 300);
mount(BranchSelector, {
it("should show the branch dropdown if branches available", () => {
render(BranchSelector, {
props: {
...defaultProps, branches: {
...defaultProps,
branches: {
"master": "e678629cd37c770c640a2cd997fc76303c815772",
"feature-branch": "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5",
"xyz": "debf82ef3623ec11751a993bda85bac2ff1c6f00",
},
branchesDropdown: true,
}, callbacks: {
branchChanged: cy.stub().as("branchChanged")
branchesDropdown: true
}
}, styles);
cy.get("div.dropdown > div").first().click();
cy.get("@branchChanged")
.should("be.calledOnce")
.its("firstCall.args.0.detail")
.should("equal", "feature-branch");
});
cy.get("div.dropdown div.dropdown-item")
.first()
.should("contain.text", "feature-branch")
.next()
.should("contain.text", "master").should("have.class", "selected")
.next()
.should("contain.text", "xyz");
});
it("Render with commit != head, passing a branch as rev and branch listing", () => {
mount(BranchSelector, {
it("should show feature-branch label and head commit, if branch label is passed as revision", () => {
render(BranchSelector, {
props: {
...defaultProps, branches: {
"master": "e678629cd37c770c640a2cd997fc76303c815772",
@@ -61,42 +83,84 @@ describe('BranchSelector', function () {
},
revision: "feature-branch"
}
}, styles);
cy.findByText("feature-branch").should("exist");
cy.get("div.commit > div.hash.desktop").should("have.text", "29e8b7b");
});
cy.get("div.stat.branch").should("be.visible").should("have.text", "feature-branch");
cy.get("div.hash.mobile").should("be.visible").should("have.text", "29e8b7b");
});
it("Render with commit != head passing a commit as rev and branch listing", () => {
mount(BranchSelector, {
it("should show only commit if no branchLabel nor branches are available", () => {
render(BranchSelector, {
props: {
...defaultProps,
revision: "debf82ef3623ec11751a993bda85bac2ff1c6f00"
revision: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
branches: {}
}
}, styles
);
cy.get("div.commit > div.hash.desktop").should("have.text", "debf82ef3623ec11751a993bda85bac2ff1c6f00");
});
cy.get("div.hash.mobile").should("be.visible").should("have.text", "debf82e");
cy.viewport("macbook-13");
cy.get("div.hash.desktop").should("be.visible").should("have.text", "debf82ef3623ec11751a993bda85bac2ff1c6f00");
});
it("Render with commit = head, without branch listing", () => {
mount(BranchSelector, {
it("should show only commit if branches are available but no branchLabel", () => {
render(BranchSelector, {
props: {
...defaultProps,
revision: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
}
});
cy.get("div.hash.mobile").should("be.visible").should("have.text", "debf82e");
cy.viewport("macbook-13");
cy.get("div.hash.desktop").should("be.visible").should("have.text", "debf82ef3623ec11751a993bda85bac2ff1c6f00");
});
it("should show defaultBranch label if revision == head", () => {
render(BranchSelector, {
props: {
...defaultProps,
revision: "e678629cd37c770c640a2cd997fc76303c815772",
branches: {},
branches: {}
}
}, styles
);
cy.get("div.commit > div.hash.desktop").should("have.text", "e678629cd37c770c640a2cd997fc76303c815772");
});
cy.get("div.stat.branch.not-allowed").should("be.visible").should("have.text", "master");
});
});
it("Render without branch listing, commit != head", () => {
mount(BranchSelector, {
describe("Layout", () => {
it("should show shortened commit when on mobile, and full hash when on desktop", () => {
render(BranchSelector, {
props: {
...defaultProps,
revision: "6b84e519d3c535879eb2b9ee8457bb70ca275a75",
branches: {},
revision: "e678629cd37c770c640a2cd997fc76303c815772"
}
}, styles);
cy.get("div.commit > div.hash.desktop").should("have.text", "6b84e519d3c535879eb2b9ee8457bb70ca275a75");
});
cy.viewport("iphone-x");
cy.get("div.hash.mobile").should("be.visible");
cy.get("div.hash.desktop").should("not.be.visible");
cy.viewport("macbook-15");
cy.get("div.hash.mobile").should("not.be.visible");
cy.get("div.hash.desktop").should("be.visible");
});
});
describe("Events", () => {
it("should dispatch a 'branchChanged' event on click", () => {
const { getByText, component } = render(BranchSelector, {
props: {
...defaultProps,
revision: "feature-branch",
branchesDropdown: true,
branches: {
"feature-branch": "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5",
"xyz": "debf82ef3623ec11751a993bda85bac2ff1c6f00",
}
}
});
const branchLabel = getByText("xyz");
const mock = cy.spy();
component.$on("branchChanged", mock);
fireEvent.click(branchLabel);
expect(mock).to.have.been.calledOnce;
});
});
+1 -1
View File
@@ -19,7 +19,7 @@
$: branchList = Object.keys(branches).sort().map(b => ({ key: b, value: b, badge: null }));
$: showSelector = branchList.length > 1;
$: head = branches[project.defaultBranch];
$: head = project.head ?? branches[project.defaultBranch];
$: commit = getOid(revision, branches) || head;
$: if (commit == head) {
branchLabel = project.defaultBranch;
+1 -1
View File
@@ -188,7 +188,7 @@
<div class="stat commit-count" class:active={content == ProjectContent.History} on:click={() => toggleContent(ProjectContent.History)}>
<strong>{tree.stats.commits}</strong> commit(s)
</div>
<div class="stat">
<div class="stat contributor-count">
<strong>{tree.stats.contributors}</strong> contributor(s)
</div>
</header>
+1 -1
View File
@@ -57,7 +57,7 @@
<div class="history">
{#each history.headers as group (group.time)}
<div class="commit-group">
<header>
<header class="commit-date">
<p>{group.time}</p>
</header>
<div class="commit-group-headers">
+65 -50
View File
@@ -1,6 +1,6 @@
import PeerSelector from "./PeerSelector.svelte";
import { mount } from "radicle-svelte-unit-test";
import { styles } from "@test/support/index";
import { fireEvent, render } from "@testing-library/svelte";
import "@public/index.css";
const defaultProps = {
peer: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
@@ -14,67 +14,82 @@ const defaultProps = {
toggleDropdown: () => console.log("toggle"),
};
describe('PeerSelector', function () {
it("Render correctly with default props", () => {
mount(PeerSelector, {
describe('Logic', function () {
it("show delegate name and badge", () => {
render(PeerSelector, {
props: defaultProps
}, styles);
cy.get("span.peer-id").should("has.text", "sebastinez");
cy.get("span.badge").should("has.text", "delegate");
});
cy.get("span.peer-id").should("have.text", "sebastinez");
cy.get("span.badge.primary").should("have.text", "delegate");
});
it("Test Peer selection", () => {
mount(PeerSelector, {
props: {
...defaultProps, peers: [
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
"name": "sebastinez",
"delegate": false
},
{
"id": "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
"name": "cloudhead",
"delegate": true
},
],
peersDropdown: true
}, callbacks: {
peerChanged: cy.stub().as("peerChanged")
}
}, styles);
cy.get("div.dropdown > div").last().click();
cy.get("@peerChanged")
.should("be.calledOnce")
.its("firstCall.args.0.detail")
.should("equal", "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1");
});
it("If no peers are provided, no dropdown should be showed", () => {
mount(PeerSelector, {
it("show peer id with badge if no name available", () => {
render(PeerSelector, {
props: {
...defaultProps,
peers: [],
peersDropdown: true
peers: [
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
"delegate": true
}
],
}
}, styles);
cy.get("div.dropdown > div.dropdown-item").should("not.exist");
});
cy.get("span.peer-id").should("have.text", "hyyg55…p7ofue");
cy.get("span.badge.primary").should("have.text", "delegate");
});
it("If peer identity is not being resolved, fallback to peer id", () => {
mount(PeerSelector, {
it("show only peer id if no additional data available", () => {
render(PeerSelector, {
props: {
...defaultProps,
peers: [
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
}
],
peersDropdown: true
]
}
}, styles);
cy.get("span.peer-id").should("has.text", "hyyg55…p7ofue");
cy.get("span.badge").should("not.exist");
cy.get("div.dropdown > .dropdown-item")
.first()
.should("contain", "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue");
});
cy.get("span.peer-id").should("have.text", "hyyg55…p7ofue");
});
});
describe("Layout", () => {
it("should highlight the current peer", () => {
render(PeerSelector, {
props: { ...defaultProps, peersDropdown: true }
});
cy.get("div.dropdown-item").should("have.class", "selected");
});
});
describe('Events', () => {
it("dispatch peerChanged event if clicking on a peer", () => {
cy.viewport("macbook-13");
const { getByText, component } = render(PeerSelector, {
props: {
...defaultProps,
peersDropdown: true,
peers: [
{
"id": "hyy841u4phudmr8s5rg1jjwd1ct7x7438wmjwtsm464y8uyxyhyi6c",
"name": "cloudhead",
"delegate": true
},
{
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
"name": "sebastinez",
"delegate": true
}
]
}
});
const peer = getByText("cloudhead");
const mock = cy.spy();
component.$on("peerChanged", mock);
fireEvent.click(peer);
expect(mock).to.have.been.calledOnce;
});
});
-48
View File
@@ -1,48 +0,0 @@
import { Seed } from "./Seed";
import { Config } from "@app/config";
describe('Seed', function () {
it("Constructs git host correctly", () => {
const cfg = new Config({ name: "homestead", chainId: 1 }, null as any, null);
const seed = new Seed({
host: "seed.cloudhead.io",
id: "hydzjm73tstmdcwhach3psfgkunbjpih7ztnuswhw9w6h9pi5sffd6",
git: "git.seed.cloudhead.io",
}, cfg);
expect(seed.git.host).to.eq("git.seed.cloudhead.io");
expect(seed.git.port).to.eq(443);
expect(seed.host).to.eq("seed.cloudhead.io");
expect(seed.api.host).to.eq(seed.host);
expect(seed.api.port).to.eq(8777);
});
it("Constructs git host correctly with custom port", () => {
const cfg = new Config({ name: "homestead", chainId: 1 }, null as any, null);
const seed = new Seed({
host: "seed.cloudhead.io",
id: "hydzjm73tstmdcwhach3psfgkunbjpih7ztnuswhw9w6h9pi5sffd6",
git: "https://git.seed.cloudhead.io:8778",
}, cfg);
expect(seed.git.host).to.eq("git.seed.cloudhead.io");
expect(seed.git.port).to.eq(8778);
});
it("Constructs api host correctly", () => {
const cfg = new Config({ name: "homestead", chainId: 1 }, null as any, null);
const seed = new Seed({
host: "seed.cloudhead.io",
id: "hydzjm73tstmdcwhach3psfgkunbjpih7ztnuswhw9w6h9pi5sffd6",
api: "https://api.seed.cloudhead.io:8080",
}, cfg);
expect(seed.api.host).to.eq("api.seed.cloudhead.io");
expect(seed.api.port).to.eq(8080);
expect(seed.host).to.eq("api.seed.cloudhead.io");
expect(seed.git.host).to.eq("seed.cloudhead.io");
expect(seed.git.port).to.eq(443);
});
});
+4 -2
View File
@@ -8,8 +8,11 @@ import WalletConnect from "@walletconnect/client";
import config from "@app/config.json";
import { WalletConnectSigner } from "./WalletConnectSigner";
declare global {
interface Window {
// eslint-disable-next-line @typescript-eslint/naming-convention
Cypress: any;
ethereum: any;
registrarState: any;
}
@@ -246,8 +249,7 @@ export async function getConfig(): Promise<Config> {
if (metamask) {
// If Metamask is detected, we use the network configured there.
const ready = await checkMetaMask(metamask);
if (ready === null) throw new Error("Not able to connect to Metamask, check for multiple Web3 providers");
network = ready;
if (ready) network = ready;
}
const networkConfig = (<Record<string, any>> config)[network.name];
+7
View File
@@ -0,0 +1,7 @@
// A simple mock for some HTMLCanvasElement functions to run in a node env.
HTMLCanvasElement.prototype.getContext = () => ({
fillRect: () => null
});
HTMLCanvasElement.prototype.toDataURL = () => null;
+9
View File
@@ -0,0 +1,9 @@
/* eslint-disable no-undef */
import fetch from 'node-fetch';
if (! globalThis.fetch) {
globalThis.fetch = fetch;
globalThis.Headers = fetch.Headers;
globalThis.Request = fetch.Request;
globalThis.Response = fetch.Response;
}
-1
View File
@@ -169,7 +169,6 @@ export function path(opts: PathOptions): string {
}
// We need a SHA1 commit in some places, so we return early if the revision is a SHA and else we look into branches.
// As fallback we use the head commit.
export function getOid(revision: string, branches?: Branches): string | null {
if (isOid(revision)) return revision;
+276
View File
@@ -0,0 +1,276 @@
import { BigNumber } from "ethers";
import { describe, expect, test } from "vitest";
import type { Config } from "./config";
import * as utils from "./utils";
describe("Conversions", () => {
test("toWei", () => {
expect(
utils.toWei("10")
).toEqual(BigNumber.from("10000000000000000000"));
});
});
describe("Format functions", () => {
test.each([
{ cid: "Qm1234567890123456789012345678901234567890", expected: "https://ipfs.io/ipfs/Qm1234567890123456789012345678901234567890" },
{ cid: undefined, expected: undefined }
])("formatIpfsFile $cid => $expected", ({ cid, expected }) => {
expect(
utils.formatIpfsFile(cid)
).toEqual(expected);
});
test.each([
{ amount: "1000", digits: 2, expected: "10.0" },
{ amount: "10000000000000000000", expected: "10.0" },
])("formatBalance", ({ amount, digits, expected }) => {
expect(utils.formatBalance(BigNumber.from(amount), digits)).toEqual(expected);
});
test("formatRadicleId", () => {
expect(
utils.formatRadicleId(
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 156, 38, 115, 99, 61, 118, 237, 10, 20, 115, 111, 188, 10, 117, 137, 59, 107, 76, 77, 86])
))
.toEqual("rad:git:hnrkjajuucc6zp5eknt3s9xykqsrus44cjimy");
});
test("formatProjectHash", () => {
expect(
utils.formatProjectHash(
new Uint8Array([17, 20, 69, 234, 87, 208, 172, 127, 33, 22, 110, 216, 52, 69, 104, 219, 88, 66, 50, 152, 115, 23])
)
).toEqual("45ea57d0ac7f21166ed8344568db584232987317");
});
test.each([
{ hash: "#L42", expected: 42 },
{ hash: "#ETH", expected: null },
])("formatLocationHash $hash => $expected", ({ hash, expected }) => {
expect(
utils.formatLocationHash(hash))
.toEqual(expected);
});
test.each([
{ id: "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1", expected: "hydkkk…coygh1" }
])("formatSeedId $id => $expected", ({ id, expected }) => {
expect(
utils.formatSeedId(id))
.toEqual(expected);
});
test("formatRadicleUrn", () => {
expect(
utils.formatRadicleUrn("rad:git:hnrkemobagsicpf9sr95o3g551otspcd84c9o"))
.toEqual("rad:git:hnrkem…d84c9o");
});
test("formatRadicleUrn throw when wrong URN", () => {
expect(() => utils.formatRadicleUrn("hnrkemobagsicpf9sr95o3g551otspcd84c9o"))
.toThrow();
});
test("formatCAIP10Address", () => {
expect(
utils.formatCAIP10Address(
"0x1234567890123456789012345678901234567890",
"eip155",
1
))
.toEqual("0x1234567890123456789012345678901234567890@eip155:1");
});
test("formatAddress", () => {
expect(
utils.formatAddress(
"0xb5d85cbf7cb3ee0d56b3bb207d5fc4b82f43f511",
)
).toEqual("b5d8 – F511");
expect(
() => utils.formatAddress(
"0x8f91813",
)
).toThrowError('invalid address (argument="address", value="0x8f91813", code=INVALID_ARGUMENT, version=address/5.5.0)');
});
test.each([
{ hash: "0x8f918133b56bb85c18ea192549503f0ea59e3beb1f88023f442656c660018e3a", expected: "0x8f91...8e3a" },
{ hash: "0x8f91813", expected: "0x8f91813" }, // If the string length is less than 10 characters the entire string is returned.
])("formatHash $hash => $expected", ({ hash, expected }) => {
expect(
utils.formatHash(hash)
).toEqual(expected);
});
test.each([
{ commit: "a8a6a979a6261a2ec1ea85fc9a65a4a30aa22cc8", expected: "a8a6a97" },
{ commit: "a8a6a97", expected: "a8a6a97" }
])("formatCommit $commit => $expected", ({ commit, expected }) => {
expect(
utils.formatCommit(commit)
).toEqual(expected);
});
});
describe("String Assertions", () => {
test.each([
{ a: "0x1234567890123456789012345678901234567890", b: "0x1234567890123456789012345678901234567890", expected: true },
{ a: "0x1234567890123456789012345678901234567890", b: "0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0", expected: false }
])("isAddressEqual", ({ a, b, expected }) => {
expect(
utils.isAddressEqual(a, b))
.toEqual(expected);
});
test.each([
{ domain: "alt-clients.radicle.xyz", expected: true },
{ domain: "0.0.0.0", expected: true }, // Pass as true since we are not in production
{ domain: "", expected: false },
])("isDomain $domain => $expected", ({ domain, expected }) => {
expect(
utils.isDomain(domain))
.toEqual(expected);
});
test.each([
{ path: "README.md", expected: true },
{ path: "README.mkd", expected: true },
{ path: "README.markdown", expected: true },
{ path: "", expected: false },
])("isMarkdownPath $path => $expected", ({ path, expected }) => {
expect(
utils.isMarkdownPath(path))
.toEqual(expected);
});
test.each([
{ id: "rad:git:hnrkemobagsicpf9sr95o3g551otspcd84c9o", expected: true },
{ id: "0x1234567890123456789012345678901234567890", expected: false },
])("isRadicleId $id => $expected", ({ id, expected }) => {
expect(
utils.isRadicleId(id))
.toEqual(expected);
});
test.each([
{ oid: "a64ae9c6d572e0ad906faa9a4a7a8d43f113278c", expected: true },
{ oid: "a64ae9c", expected: false }
])("isOid $oid => $expected", ({ oid, expected }) => {
expect(
utils.isOid(oid))
.toEqual(expected);
});
test.each([
{ address: "0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0", expected: true },
{ address: "0x5E813e48a81977c6fdd565ed5097eb600c73c4f0", expected: false }, // If address is badly checksummed => false
{ address: "0x5e813e48a81977c6fdd565ed5097eb600c73c4f0", expected: true },
])("isAddress $address => $expected", ({ address, expected }) => {
expect(
utils.isAddress(
address,
))
.toBe(expected);
});
test.each([
{ url: "https://app.radicle.network", expected: true },
{ url: "http://app.radicle.network", expected: true },
{ url: "http://app", expected: true },
{ url: "://app", expected: false },
{ url: "//app", expected: false },
{ url: "app", expected: false },
])("isUrl $url => $expected", ({ url, expected }) => {
expect(
utils.isUrl(
url,
))
.toBe(expected);
});
test.each([
{ did: "did:3:kjzl6cwe1jw1481xu9oyww9bhmueqr8f5uryk4xha9jzhj6vi063e0blpnil383", expected: true },
{ did: "did:kjzl6cwe1jw1481xu9oyww9bhmueqr8f5uryk4xha9jzhj6vi063e0blpnil383", expected: false },
])("isDid $did => $expected", ({ did, expected }) => {
expect(
utils.isDid(did))
.toBe(expected);
});
});
describe("Others", () => {
test.each([
{ viewer: "https://gnosis-safe.io/app/#/safes", name: "", expected: "https://gnosis-safe.io/app/#/safes/0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0" },
{ viewer: null, name: "", expected: "https://etherscan.io/address/0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0" },
])("safeLink $viewer => $expected", ({ name, viewer, expected }) => {
expect(
utils.safeLink(
"0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0",
{
network: {
name
},
safe: {
viewer
}
} as Config
))
.toEqual(expected);
});
test.each([
{ name: "rinkeby", expected: "https://rinkeby.etherscan.io/address/0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0" },
{ name: "", expected: "https://etherscan.io/address/0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0" },
])("explorerLink $name => $expected", ({ name, expected }) => {
expect(
utils.explorerLink(
"0x5E813e48a81977c6Fdd565ed5097eb600C73C4f0",
{
network: {
name,
}
} as Config
))
.toEqual(expected);
});
test.each([
{ id: "rad:git:hnrkjajuucc6zp5eknt3s9xykqsrus44cjimy", expected: new Uint8Array([156, 38, 115, 99, 61, 118, 237, 10, 20, 115, 111, 188, 10, 117, 137, 59, 107, 76, 77, 86]) },
{ id: "hnrkjajuucc6zp5eknt3s9xykqsrus44cjimy", expected: new Uint8Array([156, 38, 115, 99, 61, 118, 237, 10, 20, 115, 111, 188, 10, 117, 137, 59, 107, 76, 77, 86]) }
])("decodeRadicleId", ({ id, expected }) => {
expect(
utils.decodeRadicleId(id))
.toEqual(expected);
});
});
describe("Parse Strings", () => {
test.each([
{ label: "sebastinez.radicle.eth", expected: "sebastinez" },
{ label: "sebastinez", expected: "sebastinez" },
])("parseEnsLabel", ({ label, expected }) => {
expect(
utils.parseEnsLabel(
label,
{
registrar: {
address: "0x1234567890123456789012345678901234567890",
domain: "radicle.eth"
}
} as Config
)
).toEqual(expected);
});
test.each([
{ input: "https://twitter.com/cloudhead", expected: "cloudhead" },
{ input: "sebastinez", expected: "sebastinez" }
])("parseUsername", ({ input, expected }) => {
expect(
utils.parseUsername(input)
).toEqual(expected);
});
});
+4 -2
View File
@@ -98,6 +98,8 @@ export function formatSeedId(id: string): string {
}
export function formatRadicleUrn(id: string): string {
assert(isRadicleId(id));
return id.substring(0, 14)
+ '…'
+ id.substring(id.length - 6, id.length);
@@ -125,6 +127,7 @@ export function formatIpfsFile(ipfs: string | undefined): string | undefined {
return undefined;
}
// If the string is less than 10 characters the entire string is returned.
export function formatHash(hash: string): string {
if (hash.length < 10) return hash;
return hash.substring(0, 6)
@@ -149,8 +152,7 @@ export function capitalize(s: string): string {
return s[0].toUpperCase() + s.substring(1);
}
// Takes a domain name, eg. 'cloudhead.radicle.eth' and
// returns the label, eg. 'cloudhead', otherwise `undefined`.
// Takes a domain name, eg. 'cloudhead.radicle.eth' and returns the label, eg. 'cloudhead'.
export function parseEnsLabel(name: string, config: Config): string {
const domain = config.registrar.domain.replace(".", "\\.");
const label = name.replace(new RegExp(`\\.${domain}$`), "");
+2 -3
View File
@@ -1,8 +1,7 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"include": [
"src",
"cypress"
"src"
],
"exclude": [
"node_modules/*"
@@ -26,8 +25,8 @@
"importsNotUsedAsValues": "error",
"skipLibCheck": true,
"paths": {
"@public/*": ["./public/*"],
"@app/*": ["./src/*"],
"@test/*": ["./cypress/*"]
}
},
"noEmit": true
+25 -1
View File
@@ -1,3 +1,4 @@
///<reference types="vitest" />
import path from 'path';
import { UserConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
@@ -8,8 +9,26 @@ const config: UserConfig = {
optimizeDeps: {
exclude: ['svelte-routing', '@pedrouid/environment', '@pedrouid/iso-crypto']
},
test: {
deps: {
inline: [
"@ethersproject/signing-key",
"@ethersproject/basex",
]
},
environment: "happy-dom",
include: ["**/*.test.ts"],
reporters: "verbose",
coverage: {
reporter: ["html"],
all: true,
excludeNodeModules: true,
extension: [".svelte", ".ts", ".js"]
},
},
plugins: [
svelte({
hot: !process.env.VITEST,
compilerOptions: {
dev: process.env.NODE_ENV !== "production"
}
@@ -26,8 +45,8 @@ const config: UserConfig = {
alias: {
// This is needed for vite not to choke.
"caip": path.resolve("./node_modules/caip/dist/umd/index.min.js"),
'@public': path.resolve('./public'),
'@app': path.resolve('./src'),
'@test': path.resolve('./cypress'),
// Polyfill for Node.js 'stream' library.
'stream': path.resolve('./src/polyfills/stream.ts'),
'typedarray-to-buffer': path.resolve('./src/polyfills/typedarray-to-buffer.js'),
@@ -46,4 +65,9 @@ const config: UserConfig = {
}
};
// For Vitest to work we need to unset READABLE_STREAM.
if (process.env.VITEST) {
config.define = undefined;
}
export default config;