mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
Replace Cypress with Playwright
Playwright is more flexible and has better developer experience than Cypress.
We add end to end tests covering:
- project source browsing
- tree view
- commit list view
- commit view
- routing
- hash based
- history API based
- clipboard copy paste functionality
- seed view
- project search
- landing page view
- settings
Co-authored-by: Sebastian Martinez <me@sebastinez.dev>
Signed-off-by: Rūdolfs Ošiņš <rudolfs@osins.org>
This commit is contained in:
co-authored by
Sebastian Martinez
parent
d740d7e5a1
commit
9cb9332e88
@@ -3,19 +3,25 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
check-build:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- uses: actions/checkout@v2
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v4
|
||||
with:
|
||||
browser: chrome
|
||||
start: npm run serve
|
||||
wait-on: "http://localhost:4173"
|
||||
command: npx cypress run --config baseUrl="http://localhost:4173" --spec 'cypress/e2e/home.spec.ts'
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Build app
|
||||
run: npm run build
|
||||
- name: Run Playwright build smoke test
|
||||
run: npm run test:e2e -- --project chromium --config playwright.buildSmoke.config.ts tests/e2e/buildSmoke.spec.ts
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: test-artifacts-${{ runner.os }}
|
||||
retention-days: 30
|
||||
path: |
|
||||
tests/artifacts/**/*
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: check-e2e
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
check-e2e:
|
||||
strategy:
|
||||
matrix:
|
||||
browser: [chromium, firefox]
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Start http-api test server
|
||||
run: ./scripts/run-http-api-with-fixtures --non-interactive --detach
|
||||
- name: Run Playwright tests
|
||||
run: npm run test:e2e -- --project ${{ matrix.browser }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: test-artifacts-${{ runner.os }}
|
||||
retention-days: 30
|
||||
path: |
|
||||
tests/artifacts/**/*
|
||||
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
name: check-integration-test
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
chrome:
|
||||
runs-on: ubuntu-latest
|
||||
name: E2E on Chrome
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v4
|
||||
with:
|
||||
browser: chrome
|
||||
start: npm start
|
||||
wait-on: 'http://localhost:3000'
|
||||
command: npm run test:e2e
|
||||
|
||||
@@ -2,15 +2,14 @@ name: check-unit-test
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
check-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- run: npm ci
|
||||
- run: ./scripts/unit-test
|
||||
shell: bash
|
||||
- run: npm run test:unit
|
||||
|
||||
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.12.1'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@ KaTeX_**.woff2
|
||||
public/twemoji/*.svg
|
||||
|
||||
# Integration Tests
|
||||
cypress/screenshots
|
||||
tests/tmp/**/*
|
||||
tests/artifacts/**/*
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { defineConfig } from "cypress";
|
||||
|
||||
export default defineConfig({
|
||||
video: false,
|
||||
defaultCommandTimeout: 10000,
|
||||
retries: {
|
||||
runMode: 2,
|
||||
openMode: 0,
|
||||
},
|
||||
e2e: {
|
||||
specPattern: "cypress/e2e/**/*spec.ts",
|
||||
baseUrl: "http://localhost:3000",
|
||||
},
|
||||
component: {
|
||||
devServer: {
|
||||
framework: "svelte",
|
||||
bundler: "vite",
|
||||
},
|
||||
specPattern: "src/**/*spec.ts",
|
||||
},
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
import { MockProvider } from "@rsksmart/mock-web3-provider";
|
||||
|
||||
describe("landing page", () => {
|
||||
it("displays correctly projects", () => {
|
||||
cy.intercept(
|
||||
{ pathname: "/v1/projects/*" },
|
||||
{ fixture: "projectInfo.json" },
|
||||
).as("projectInfo");
|
||||
cy.intercept(
|
||||
{ pathname: "/v1/projects/*/activity" },
|
||||
{ fixture: "projectActivity.json" },
|
||||
).as("projectActivity");
|
||||
cy.visit("/", {
|
||||
onBeforeLoad(win) {
|
||||
const address = "0xB98bD7C7f656290071E52D1aA617D9cB4467Fd6D";
|
||||
const privateKey =
|
||||
"de926db3012af759b4f24b5a51ef6afa397f04670f634aa4f48d4480417007f3";
|
||||
win.ethereum = new MockProvider({
|
||||
address,
|
||||
privateKey,
|
||||
networkVersion: 1,
|
||||
});
|
||||
},
|
||||
});
|
||||
cy.wait(["@projectInfo", "@projectActivity"]);
|
||||
cy.get(".project .name")
|
||||
.first()
|
||||
.should("have.text", "bright-forest-protocol");
|
||||
});
|
||||
});
|
||||
@@ -1,174 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/// <reference types="cypress" />
|
||||
import { MockProvider } from "@rsksmart/mock-web3-provider";
|
||||
import { getPath } from "../support/e2e";
|
||||
|
||||
const groupedCommits = [
|
||||
{
|
||||
groupDate: "Thursday, March 3, 2022",
|
||||
commits: [
|
||||
{
|
||||
header: {
|
||||
sha: "9cd3532",
|
||||
summary: "Second commit",
|
||||
description: "",
|
||||
committer: {
|
||||
name: "dabit3",
|
||||
mail: "dabit3@gmail.com",
|
||||
},
|
||||
committerTime: "06:51 GMT+1",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupDate: "Wednesday, March 2, 2022",
|
||||
commits: [
|
||||
{
|
||||
header: {
|
||||
sha: "e045b92",
|
||||
summary: "Update README",
|
||||
description: "",
|
||||
committer: {
|
||||
name: "dabit3",
|
||||
mail: "dabit3@gmail.com",
|
||||
},
|
||||
committerTime: "17:14 GMT+1",
|
||||
},
|
||||
},
|
||||
{
|
||||
header: {
|
||||
sha: "cbf5df4",
|
||||
summary: "initial commit",
|
||||
description: "this is the first commit of many",
|
||||
committer: {
|
||||
name: "dabit3",
|
||||
mail: "dabit3@gmail.com",
|
||||
},
|
||||
committerTime: "16:58 GMT+1",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
describe("project commits", () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept("/", {
|
||||
fixture: "projectHome.json",
|
||||
}).as("seedHome");
|
||||
cy.intercept("v1/peer", {
|
||||
fixture: "projectPeer.json",
|
||||
}).as("seedPeer");
|
||||
cy.intercept("v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy", {
|
||||
fixture: "projectInfo.json",
|
||||
}).as("seedInfo");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes",
|
||||
{ fixture: "projectRemotes.json" },
|
||||
).as("seedRemotes");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/56e4e029c294b08546386e1fb706b772c7433c49/",
|
||||
{ fixture: "projectTree56e4e02.json" },
|
||||
).as("seedTree56e4e02");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/cbf5df499ab4f4a908f1756fbe2c236a4530516a/",
|
||||
{ fixture: "projectTreecbf5df4.json" },
|
||||
).as("seedTreecbf5df4");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commits?parent=56e4e029c294b08546386e1fb706b772c7433c49&verified=true",
|
||||
{ fixture: "projectCommits.json" },
|
||||
).as("seedCommits");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commits/cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
{ fixture: "projectCommit.json" },
|
||||
).as("seedCommit");
|
||||
});
|
||||
|
||||
it("display commit groups and commit trailers", () => {
|
||||
cy.visit(
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/history",
|
||||
{
|
||||
onBeforeLoad(win) {
|
||||
const address = "0xB98bD7C7f656290071E52D1aA617D9cB4467Fd6D";
|
||||
const privateKey =
|
||||
"de926db3012af759b4f24b5a51ef6afa397f04670f634aa4f48d4480417007f3";
|
||||
win.ethereum = new MockProvider({
|
||||
address,
|
||||
privateKey,
|
||||
networkVersion: 1,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
cy.wait([
|
||||
"@seedHome",
|
||||
"@seedPeer",
|
||||
"@seedInfo",
|
||||
"@seedRemotes",
|
||||
"@seedTree56e4e02",
|
||||
"@seedCommits",
|
||||
]);
|
||||
// This iterates over the commit groups and then over each commit.
|
||||
cy.get(".commit-group")
|
||||
.should("have.length", 2)
|
||||
.each((item, index) => {
|
||||
expect(Cypress.$(item.children(".commit-date")).text()).to.eq(
|
||||
groupedCommits[index].groupDate,
|
||||
);
|
||||
const $el = Cypress.$(item.find(".commit-teaser"));
|
||||
cy.wrap($el).each((commit, commitIndex) => {
|
||||
expect(Cypress.$(commit).find(".hash").text()).to.eq(
|
||||
groupedCommits[index].commits[commitIndex].header.sha,
|
||||
);
|
||||
expect(Cypress.$(commit).find(".summary").text()).to.eq(
|
||||
groupedCommits[index].commits[commitIndex].header.summary,
|
||||
);
|
||||
expect(Cypress.$(commit).find(".committer").first().text()).to.eq(
|
||||
groupedCommits[index].commits[commitIndex].header.committer.name,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
cy.get(".commit-teaser .badge").last().trigger("mouseenter");
|
||||
// Checking that the initial commit has the Verified badge
|
||||
cy.get(".popup .header").should(
|
||||
"have.text",
|
||||
"✔ This commit was signed\n with the committer's radicle key.",
|
||||
);
|
||||
cy.get(".popup .peer").should(
|
||||
"contain.text",
|
||||
"hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
);
|
||||
cy.get(".popup .committer").should("contain.text", "dabit3");
|
||||
|
||||
cy.get(".commit").last().click();
|
||||
});
|
||||
|
||||
it("display commit details", () => {
|
||||
cy.location().should(location => {
|
||||
expect(getPath(location)).to.eq(
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commits/cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
);
|
||||
});
|
||||
cy.get("header .summary .txt-medium").should("have.text", "initial commit");
|
||||
cy.get(".commit pre.description").should(
|
||||
"have.text",
|
||||
"this is the first commit of many",
|
||||
);
|
||||
cy.get("header .committer").should("have.text", "dabit3");
|
||||
cy.get("div.changeset-summary").should(
|
||||
"have.text",
|
||||
"1 file(s) changed, 1 file(s) created, 1 file(s) deleted\n with\n 0 addition(s)\n and\n 0 deletion(s)",
|
||||
);
|
||||
cy.get("header.file-header:nth-child(1) p")
|
||||
.first()
|
||||
.should("have.text", "test.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",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,181 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/// <reference types="cypress" />
|
||||
import { MockProvider } from "@rsksmart/mock-web3-provider";
|
||||
import { getPath } from "../support/e2e";
|
||||
|
||||
describe("project header", () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept("/", {
|
||||
fixture: "projectHome.json",
|
||||
}).as("projectHome");
|
||||
cy.intercept("v1/peer", {
|
||||
fixture: "projectPeer.json",
|
||||
}).as("projectPeer");
|
||||
cy.intercept("v1/projects/bright-forest-protocol", {
|
||||
fixture: "projectInfo.json",
|
||||
}).as("projectInfo");
|
||||
cy.intercept("v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy", {
|
||||
fixture: "projectInfo.json",
|
||||
}).as("projectInfo");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes",
|
||||
{ fixture: "projectRemotes.json" },
|
||||
).as("projectRemotes");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/issues",
|
||||
{ fixture: "projectIssues.json" },
|
||||
).as("projectIssues");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/patches",
|
||||
{ fixture: "projectPatches.json" },
|
||||
).as("projectPatches");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/56e4e029c294b08546386e1fb706b772c7433c49",
|
||||
{ fixture: "projectTree56e4e02.json" },
|
||||
).as("projectTree56e4e02");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
{ fixture: "projectTreecbf5df4.json" },
|
||||
).as("projectTreecbf5df4");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke",
|
||||
{ fixture: "projectBranches.json" },
|
||||
).as("projectBranches");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/readme/56e4e029c294b08546386e1fb706b772c7433c49",
|
||||
{ fixture: "projectReadme.json" },
|
||||
).as("projectReadme");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commits?parent=cbf5df499ab4f4a908f1756fbe2c236a4530516a?verified=true",
|
||||
{ fixture: "projectCommits.json" },
|
||||
).as("projectCommits");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/readme/cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
{ fixture: "projectReadme.json" },
|
||||
).as("projectReadme");
|
||||
cy.intercept(
|
||||
"v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/commit/cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
{ fixture: "projectCommit.json" },
|
||||
).as("projectCommit");
|
||||
});
|
||||
it("renders header correctly", () => {
|
||||
cy.visit("/seeds/willow.radicle.garden/bright-forest-protocol", {
|
||||
onBeforeLoad(win) {
|
||||
const address = "0xB98bD7C7f656290071E52D1aA617D9cB4467Fd6D";
|
||||
const privateKey =
|
||||
"de926db3012af759b4f24b5a51ef6afa397f04670f634aa4f48d4480417007f3";
|
||||
win.ethereum = new MockProvider({
|
||||
address,
|
||||
privateKey,
|
||||
networkVersion: 1,
|
||||
});
|
||||
},
|
||||
});
|
||||
cy.wait([
|
||||
"@projectHome",
|
||||
"@projectPeer",
|
||||
"@projectInfo",
|
||||
"@projectRemotes",
|
||||
"@projectTree56e4e02",
|
||||
"@projectReadme",
|
||||
]);
|
||||
cy.get('[aria-label="Seed"] span').should(
|
||||
"have.text",
|
||||
"willow.radicle.garden",
|
||||
);
|
||||
cy.get('[aria-label="Commit count"]').should(
|
||||
"have.text",
|
||||
"3\n commit(s)",
|
||||
);
|
||||
cy.get('[aria-label="Contributor count"]').should(
|
||||
"have.text",
|
||||
"1\n contributor(s)",
|
||||
);
|
||||
cy.get("div.stat.branch")
|
||||
.should("have.class", "not-allowed")
|
||||
.should("have.text", "main");
|
||||
cy.get("div.hash.layout-desktop").should("have.text", "56e4e02");
|
||||
cy.get("div.clone-button").click();
|
||||
});
|
||||
|
||||
it("lets user change peer", () => {
|
||||
cy.get("div.selector").click();
|
||||
cy.get("div.dropdown-item")
|
||||
.contains(
|
||||
"dabit3 hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke delegate",
|
||||
)
|
||||
.click();
|
||||
cy.wait([
|
||||
"@projectHome",
|
||||
"@projectPeer",
|
||||
"@projectInfo",
|
||||
"@projectRemotes",
|
||||
"@projectBranches",
|
||||
"@projectTree56e4e02",
|
||||
"@projectReadme",
|
||||
]);
|
||||
cy.location().should(location => {
|
||||
expect(getPath(location)).to.eq(
|
||||
"/seeds/willow.radicle.garden/bright-forest-protocol/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");
|
||||
});
|
||||
|
||||
it("lets user on a specific peer change branches", () => {
|
||||
cy.get("div.commit div.stat.branch").click();
|
||||
cy.get("div.dropdown-item")
|
||||
.first()
|
||||
.contains("main")
|
||||
.next()
|
||||
.contains("master")
|
||||
.click();
|
||||
cy.wait(["@projectTreecbf5df4", "@projectReadme"]);
|
||||
cy.location().should(location => {
|
||||
expect(getPath(location)).to.eq(
|
||||
"/seeds/willow.radicle.garden/bright-forest-protocol/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/tree/master",
|
||||
);
|
||||
});
|
||||
cy.get("div.stat.branch").should("have.text", "master");
|
||||
cy.get("div.hash.layout-desktop").should("have.text", "cbf5df4");
|
||||
});
|
||||
|
||||
it("navigate to commit history", () => {
|
||||
cy.get('[aria-label="Commit count"]')
|
||||
.should("not.have.class", "active")
|
||||
.click();
|
||||
cy.wait(["@projectCommits"]);
|
||||
cy.location().should(location => {
|
||||
expect(getPath(location)).to.eq(
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/history/master",
|
||||
);
|
||||
});
|
||||
cy.get('[aria-label="Commit count"]').should("have.class", "active");
|
||||
});
|
||||
|
||||
it("navigate to issues listing", () => {
|
||||
cy.get('[aria-label="Issue count"]').click();
|
||||
cy.wait(["@projectIssues"]);
|
||||
cy.location().should(location => {
|
||||
expect(getPath(location)).to.eq(
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/issues",
|
||||
);
|
||||
});
|
||||
cy.get('[aria-label="Issue count"]').should("have.class", "active");
|
||||
});
|
||||
|
||||
it("navigate to patches listing", () => {
|
||||
cy.get('[aria-label="Patch count"]').click();
|
||||
cy.wait(["@projectPatches"]);
|
||||
cy.location().should(location => {
|
||||
expect(getPath(location)).to.eq(
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes/hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke/patches",
|
||||
);
|
||||
});
|
||||
cy.get('[aria-label="Patch count"]').should("have.class", "active");
|
||||
});
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/// <reference types="cypress" />
|
||||
import { MockProvider } from "@rsksmart/mock-web3-provider";
|
||||
|
||||
describe("project meta", () => {
|
||||
it("displays the correct project information", () => {
|
||||
cy.intercept("https://willow.radicle.garden:8777/", {
|
||||
fixture: "projectHome.json",
|
||||
}).as("projectHome");
|
||||
cy.intercept("https://willow.radicle.garden:8777/v1/peer", {
|
||||
fixture: "projectPeer.json",
|
||||
}).as("projectPeer");
|
||||
cy.intercept(
|
||||
"https://willow.radicle.garden:8777/v1/projects/bright-forest-protocol",
|
||||
{ fixture: "projectInfo.json" },
|
||||
).as("projectInfo");
|
||||
cy.intercept(
|
||||
"https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/remotes",
|
||||
{ fixture: "projectRemotes.json" },
|
||||
).as("projectRemotes");
|
||||
cy.intercept(
|
||||
"https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/tree/56e4e029c294b08546386e1fb706b772c7433c49",
|
||||
{ fixture: "projectTree56e4e02.json" },
|
||||
).as("projectTree56e4e02");
|
||||
cy.intercept(
|
||||
"https://willow.radicle.garden:8777/v1/projects/rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy/readme/56e4e029c294b08546386e1fb706b772c7433c49",
|
||||
{ fixture: "projectReadme.json" },
|
||||
).as("projectReadme");
|
||||
cy.visit("/seeds/willow.radicle.garden/bright-forest-protocol", {
|
||||
onBeforeLoad(win) {
|
||||
const address = "0xB98bD7C7f656290071E52D1aA617D9cB4467Fd6D";
|
||||
const privateKey =
|
||||
"de926db3012af759b4f24b5a51ef6afa397f04670f634aa4f48d4480417007f3";
|
||||
win.ethereum = new MockProvider({
|
||||
address,
|
||||
privateKey,
|
||||
networkVersion: 1,
|
||||
});
|
||||
},
|
||||
});
|
||||
cy.wait([
|
||||
"@projectHome",
|
||||
"@projectPeer",
|
||||
"@projectInfo",
|
||||
"@projectRemotes",
|
||||
"@projectTree56e4e02",
|
||||
]);
|
||||
cy.get("div.title").contains("bright-forest-protocol");
|
||||
cy.get("div.urn").contains("rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy");
|
||||
cy.get("div.description").contains("bfc-sc");
|
||||
cy.get("div.column-right article div.markdown h1").should(
|
||||
"have.text",
|
||||
"Basic Sample Hardhat Project",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
[
|
||||
{
|
||||
"groupDate": "Thursday, March 3, 2022",
|
||||
"commits": [
|
||||
{
|
||||
"header": {
|
||||
"sha": "9cd3532",
|
||||
"summary": "Second commit",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "dabit3",
|
||||
"mail": "dabit3@gmail.com"
|
||||
},
|
||||
"committerTime": "06:51 GMT+1"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"groupDate": "Wednesday, March 2, 2022",
|
||||
"commits": [
|
||||
{
|
||||
"header": {
|
||||
"sha": "e045b92",
|
||||
"summary": "Update README",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "dabit3",
|
||||
"mail": "dabit3@gmail.com"
|
||||
},
|
||||
"committerTime": "17:14 GMT+1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"sha": "cbf5df4",
|
||||
"summary": "initial commit",
|
||||
"description": "this is the first commit of many",
|
||||
"committer": {
|
||||
"name": "dabit3",
|
||||
"mail": "dabit3@gmail.com"
|
||||
},
|
||||
"committerTime": "16:58 GMT+1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"activity": []
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"heads": {
|
||||
"master": "cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
"main": "56e4e029c294b08546386e1fb706b772c7433c49"
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"path": "test.md",
|
||||
"diff": {
|
||||
"type": "plain",
|
||||
"hunks": [
|
||||
{
|
||||
"header": "@@ -0,0 +1 @@\n",
|
||||
"lines": [
|
||||
{
|
||||
"type": "addition",
|
||||
"line": "Hello world\n",
|
||||
"lineNum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"deleted": [
|
||||
{
|
||||
"path": "test.md",
|
||||
"diff": {
|
||||
"type": "plain",
|
||||
"hunks": [
|
||||
{
|
||||
"header": "@@ -0,0 +1 @@\n",
|
||||
"lines": [
|
||||
{
|
||||
"type": "addition",
|
||||
"line": "Hello world\n",
|
||||
"lineNum": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"]
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
{
|
||||
"headers": [
|
||||
{
|
||||
"header": {
|
||||
"sha1": "cbf5df499ab4f4a908f1756fbe2c236a4530516a",
|
||||
"author": {
|
||||
"name": "dabit3",
|
||||
"email": "dabit3@gmail.com"
|
||||
},
|
||||
"summary": "initial commit",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "dabit3",
|
||||
"email": "dabit3@gmail.com"
|
||||
},
|
||||
"committerTime": 1646236685
|
||||
},
|
||||
"context": {
|
||||
"committer": {
|
||||
"peer": {
|
||||
"id": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
"person": {
|
||||
"name": "dabit3"
|
||||
},
|
||||
"delegate": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"sha1": "e045b92297d21a709f3dea84d692c482ee61f6b2",
|
||||
"author": {
|
||||
"name": "dabit3",
|
||||
"email": "dabit3@gmail.com"
|
||||
},
|
||||
"summary": "Update README",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "dabit3",
|
||||
"email": "dabit3@gmail.com"
|
||||
},
|
||||
"committerTime": 1646237685
|
||||
},
|
||||
"context": {
|
||||
"committer": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"sha1": "9cd353282bec77fef5dd56227173fc675d2363ce",
|
||||
"author": {
|
||||
"name": "dabit3",
|
||||
"email": "dabit3@gmail.com"
|
||||
},
|
||||
"summary": "Second commit",
|
||||
"description": "",
|
||||
"committer": {
|
||||
"name": "dabit3",
|
||||
"email": "dabit3@gmail.com"
|
||||
},
|
||||
"committerTime": 1646286685
|
||||
},
|
||||
"context": {
|
||||
"committer": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"commits": 3,
|
||||
"branches": 1,
|
||||
"contributors": 1
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"urn": "rad:git:hnrk8mbpirp7ua7sy66o4t9soasbq4y8uwgoy",
|
||||
"name": "bright-forest-protocol",
|
||||
"description": "bfc-sc",
|
||||
"defaultBranch": "main",
|
||||
"delegates": [
|
||||
{
|
||||
"type": "indirect",
|
||||
"urn": "rad:git:hnrkqz68g6nddigodpgjmc1u8ydpzb8fqq8ro",
|
||||
"ids": ["hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke"]
|
||||
}
|
||||
],
|
||||
"head": "56e4e029c294b08546386e1fb706b772c7433c49",
|
||||
"patches": 2,
|
||||
"issues": 3
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "hnrk8qmheqbwxius3brqxohqrfmgdrphtirxo",
|
||||
"author": {
|
||||
"peer": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
"urn": "rad:git:hnrk81wcokr48mkm544kh74kc9fqz84d3rfcy",
|
||||
"profile": {
|
||||
"name": "sebastinez"
|
||||
}
|
||||
},
|
||||
"title": "Testing strategy",
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"comment": {
|
||||
"author": {
|
||||
"peer": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
"urn": "rad:git:hnrk81wcokr48mkm544kh74kc9fqz84d3rfcy",
|
||||
"profile": {
|
||||
"name": "sebastinez"
|
||||
}
|
||||
},
|
||||
"body": "We should define the scope we want to test in integration vs component testing (I don't mention E2E here on purpose since in our E2E tests we stubbed all responses and never hit a real server which equals to integration testing).\n\nWhile integration testing allows us to traverse the application and test navigation, component testing allows us to mount a component in isolation and check all the possible edge cases and possible regressions, and unit testing goes one step further to just individual functions on their correctness.\n\nIntegration testing npm run test:e2e eventually should be npm run test:integration\n\n**Connection through mocked Web3Provider**\n- Navigation through different URLs (e.g. the project pages, commits, issues, patches, etc.)\n- Assert the URLs we're hitting and that they update correctly, when state changes\n- Assert the least amount of display logic (we should leave that to component testing)\n If we are able to navigate to the correct URL and get the correct props, we shouldn't need to assert every piece of text.\nComponent testing npm run test:components\n\n- Assert the correct rendering\n- Test that the state of the component updates accordingly where applicable.\n**Unit tests npm run test:unit**\n\n- Util functions\nClasses (static and methods)\nI'll keep updating this issue.\nP.S.: Testing is not my expertise so any feedback is welcome.",
|
||||
"reactions": {},
|
||||
"replies": null,
|
||||
"timestamp": 1656509367
|
||||
},
|
||||
"discussion": [],
|
||||
"labels": ["discussion"],
|
||||
"timestamp": 1656509367
|
||||
},
|
||||
{
|
||||
"id": "hnrkj4c35uoyceb3d1dsscx8qq55cikrd1aio",
|
||||
"author": {
|
||||
"peer": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
"urn": "rad:git:hnrk81wcokr48mkm544kh74kc9fqz84d3rfcy",
|
||||
"profile": {
|
||||
"name": "sebastinez"
|
||||
}
|
||||
},
|
||||
"title": "Update README",
|
||||
"state": {
|
||||
"status": "closed"
|
||||
},
|
||||
"comment": {
|
||||
"author": {
|
||||
"peer": "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
"urn": "rad:git:hnrk81wcokr48mkm544kh74kc9fqz84d3rfcy",
|
||||
"profile": {
|
||||
"name": "sebastinez"
|
||||
}
|
||||
},
|
||||
"body": "We should define the scope we want to test in integration vs component testing (I don't mention E2E here on purpose since in our E2E tests we stubbed all responses and never hit a real server which equals to integration testing).\n\nWhile integration testing allows us to traverse the application and test navigation, component testing allows us to mount a component in isolation and check all the possible edge cases and possible regressions, and unit testing goes one step further to just individual functions on their correctness.\n\nIntegration testing npm run test:e2e eventually should be npm run test:integration\n\n**Connection through mocked Web3Provider**\n- Navigation through different URLs (e.g. the project pages, commits, issues, patches, etc.)\n- Assert the URLs we're hitting and that they update correctly, when state changes\n- Assert the least amount of display logic (we should leave that to component testing)\n If we are able to navigate to the correct URL and get the correct props, we shouldn't need to assert every piece of text.\nComponent testing npm run test:components\n\n- Assert the correct rendering\n- Test that the state of the component updates accordingly where applicable.\n**Unit tests npm run test:unit**\n\n- Util functions\nClasses (static and methods)\nI'll keep updating this issue.\nP.S.: Testing is not my expertise so any feedback is welcome.",
|
||||
"reactions": {},
|
||||
"replies": null,
|
||||
"timestamp": 1656509367
|
||||
},
|
||||
"discussion": [],
|
||||
"labels": ["discussion"],
|
||||
"timestamp": 1656509367
|
||||
}
|
||||
]
|
||||
@@ -1,16 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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,88 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "hnrkj4c35uoyceb3d1dsscx8qq55cikrd1aio",
|
||||
"author": {
|
||||
"peer": "hybepksbf5xzew3ztear7k7peddho5wfts3d8uyb9pcdu6sqgnwqak",
|
||||
"urn": "rad:git:hnrkkxqo5jgx3bwbphdsmqyk9djkfr368czio",
|
||||
"profile": {
|
||||
"name": "Scooby",
|
||||
"ens": null
|
||||
}
|
||||
},
|
||||
"title": "this is a patch with multiple commits and a merge base",
|
||||
"state": "proposed",
|
||||
"target": "upstream",
|
||||
"labels": [],
|
||||
"revisions": [
|
||||
{
|
||||
"id": "d2a46e1f-88a9-4e72-bc68-3f6afc92003c",
|
||||
"peer": "hybepksbf5xzew3ztear7k7peddho5wfts3d8uyb9pcdu6sqgnwqak",
|
||||
"base": "58c1864cb6c6edd8682830d087bdcef7902cf62a",
|
||||
"oid": "a36d8df141a0d3cedf47c4a42383ed98818195de",
|
||||
"comment": {
|
||||
"author": {
|
||||
"peer": "hybepksbf5xzew3ztear7k7peddho5wfts3d8uyb9pcdu6sqgnwqak",
|
||||
"urn": "rad:git:hnrkkxqo5jgx3bwbphdsmqyk9djkfr368czio",
|
||||
"profile": {
|
||||
"name": "Scooby",
|
||||
"ens": null
|
||||
}
|
||||
},
|
||||
"body": "Signed-off-by: Sebastian Martinez <me@sebastinez.dev>",
|
||||
"reactions": {},
|
||||
"replies": null,
|
||||
"timestamp": 1656666433
|
||||
},
|
||||
"discussion": [],
|
||||
"reviews": {},
|
||||
"merges": [],
|
||||
"changeset": null,
|
||||
"timestamp": 1656666433
|
||||
}
|
||||
],
|
||||
"timestamp": 1656666433
|
||||
},
|
||||
{
|
||||
"id": "hnrkqjf6ucd3o8ffztt7yyeatuj9u4k4957xo",
|
||||
"author": {
|
||||
"peer": "hybepksbf5xzew3ztear7k7peddho5wfts3d8uyb9pcdu6sqgnwqak",
|
||||
"urn": "rad:git:hnrkkxqo5jgx3bwbphdsmqyk9djkfr368czio",
|
||||
"profile": {
|
||||
"name": "Scooby",
|
||||
"ens": null
|
||||
}
|
||||
},
|
||||
"title": "Removing files",
|
||||
"state": "proposed",
|
||||
"target": "upstream",
|
||||
"labels": [],
|
||||
"revisions": [
|
||||
{
|
||||
"id": "7edbb9df-e921-4dff-b2e2-5b0a50c51188",
|
||||
"peer": "hybepksbf5xzew3ztear7k7peddho5wfts3d8uyb9pcdu6sqgnwqak",
|
||||
"base": "58c1864cb6c6edd8682830d087bdcef7902cf62a",
|
||||
"oid": "6da5af5cf09b5db638b8a6f5ce386391c3b74da4",
|
||||
"comment": {
|
||||
"author": {
|
||||
"peer": "hybepksbf5xzew3ztear7k7peddho5wfts3d8uyb9pcdu6sqgnwqak",
|
||||
"urn": "rad:git:hnrkkxqo5jgx3bwbphdsmqyk9djkfr368czio",
|
||||
"profile": {
|
||||
"name": "Scooby",
|
||||
"ens": null
|
||||
}
|
||||
},
|
||||
"body": "Signed-off-by: Sebastian Martinez <me@sebastinez.dev>",
|
||||
"reactions": {},
|
||||
"replies": null,
|
||||
"timestamp": 1656667614
|
||||
},
|
||||
"discussion": [],
|
||||
"reviews": {},
|
||||
"merges": [],
|
||||
"changeset": null,
|
||||
"timestamp": 1656667614
|
||||
}
|
||||
],
|
||||
"timestamp": 1656667614
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
{ "id": "hyy841u4phudmr8s5rg1jjwd1ct7x7438wmjwtsm464y8uyxyhyi6c" }
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
[
|
||||
{
|
||||
"id": "hyndc7nx9keq76p1bkw9831arcndeeu3trwsc7kxt3osmpi6j9oeke",
|
||||
"person": { "name": "dabit3" },
|
||||
"delegate": true
|
||||
}
|
||||
]
|
||||
@@ -1,100 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="/typography.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/colors.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/elevations.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/layout.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/index.css" />
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-cy-root></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
import { mount } from "cypress/svelte";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
//@ts-expect-error We need Buffer on the window object in the test env for component testing
|
||||
window.Buffer = Buffer;
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
mount: typeof mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add("mount", mount);
|
||||
@@ -1,13 +0,0 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
ethereum: any;
|
||||
localStorage: Storage;
|
||||
}
|
||||
}
|
||||
|
||||
function getPath(location: Location): string {
|
||||
const url = location.href.replace(window.origin, "");
|
||||
return process.env.hashRouting ? url.substring(2) : url;
|
||||
}
|
||||
|
||||
export { getPath };
|
||||
Generated
+253
-2327
File diff suppressed because it is too large
Load Diff
+13
-10
@@ -10,28 +10,33 @@
|
||||
"check": "scripts/check",
|
||||
"format": "npx prettier '**/*.@(ts|js|svelte|json|css|html)' --ignore-path .gitignore --write",
|
||||
"test:unit": "TZ='UTC' vitest run",
|
||||
"test:components": "cypress run --component",
|
||||
"test:e2e": "cypress run",
|
||||
"test:open": "cypress open"
|
||||
"test:e2e": "TZ='UTC' playwright test"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.12.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@rsksmart/mock-web3-provider": "^1.0.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.3.0",
|
||||
"@sinonjs/fake-timers": "^10.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.3.1",
|
||||
"@tsconfig/svelte": "^3.0.0",
|
||||
"@types/dompurify": "^2.4.0",
|
||||
"@types/katex": "^0.14.0",
|
||||
"@types/lodash": "^4.14.189",
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"cypress": "^10.11.0",
|
||||
"@types/lodash": "^4.14.190",
|
||||
"@types/marked": "^4.0.7",
|
||||
"@types/md5": "^2.3.2",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/sinonjs__fake-timers": "^8.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
"chalk": "^5.1.2",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"svelte-check": "^2.9.2",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^3.2.4",
|
||||
"vite-plugin-rewrite-all": "^1.0.0",
|
||||
@@ -41,8 +46,6 @@
|
||||
"@ethersproject/abstract-provider": "^5.4.0",
|
||||
"@radicle/gray-matter": "4.1.0",
|
||||
"@stardazed/streams": "^3.1.0",
|
||||
"@types/marked": "^4.0.7",
|
||||
"@types/md5": "^2.3.2",
|
||||
"@walletconnect/client": "^1.8.0",
|
||||
"buffer": "^6.0.3",
|
||||
"dompurify": "^2.4.1",
|
||||
@@ -51,7 +54,7 @@
|
||||
"katex": "^0.16.3",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^7.14.1",
|
||||
"marked": "^4.2.2",
|
||||
"marked": "^4.2.3",
|
||||
"md5": "^2.3.0",
|
||||
"plausible-tracker": "^0.3.8",
|
||||
"pure-svg-code": "^1.0.6",
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||
|
||||
import base from "./playwright.config.js";
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
...base,
|
||||
use: {
|
||||
...base.use,
|
||||
baseURL: "http://localhost:4173",
|
||||
},
|
||||
retries: 0,
|
||||
globalSetup: undefined,
|
||||
webServer: {
|
||||
command: "npm run serve",
|
||||
port: 4173,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { PlaywrightTestConfig } from "@playwright/test";
|
||||
import { devices } from "@playwright/test";
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: "./tests/e2e",
|
||||
timeout: 30_000,
|
||||
expect: {
|
||||
timeout: 8000,
|
||||
},
|
||||
fullyParallel: true,
|
||||
outputDir: "./tests/artifacts",
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
reporter: "list",
|
||||
globalSetup: "./tests/support/globalSetup",
|
||||
use: {
|
||||
actionTimeout: 0,
|
||||
baseURL: "http://localhost:3000",
|
||||
trace: "retain-on-failure",
|
||||
},
|
||||
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "firefox",
|
||||
use: {
|
||||
...devices["Desktop Firefox"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "webkit",
|
||||
use: {
|
||||
...devices["Desktop Safari"],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
webServer: {
|
||||
command: "npm run start",
|
||||
port: 3000,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
function cleanup {
|
||||
docker kill radicle-git-server-test
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
|
||||
PASSPHRASE=asdf
|
||||
REV=40bdc662b2d48cbfaa87182b21457ef8f861b04a
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
ID=$(echo $RANDOM | md5sum | head -c 8)
|
||||
BASE_PATH=$REPO_ROOT/tests/tmp/create-seed-fixture-$ID
|
||||
|
||||
TEST_REPO_ARCHIVE=$REPO_ROOT/tests/fixtures/repos/source-browsing.tar.bz2
|
||||
TEST_REPO_NAME=source-browsing
|
||||
TEST_REPO_PATH=$BASE_PATH/repos/$TEST_REPO_NAME
|
||||
|
||||
PALM_RAD_HOME=$BASE_PATH/seeds/palm
|
||||
ALICE_RAD_HOME=$BASE_PATH/peers/alice
|
||||
ALICE_CHECKOUT=$BASE_PATH/checkout/alice
|
||||
BOB_RAD_HOME=$BASE_PATH/peers/bob
|
||||
BOB_CHECKOUT=$BASE_PATH/checkout/bob
|
||||
|
||||
mkdir -p $PALM_RAD_HOME
|
||||
mkdir -p $ALICE_RAD_HOME
|
||||
mkdir -p $ALICE_CHECKOUT
|
||||
mkdir -p $BOB_RAD_HOME
|
||||
mkdir -p $BOB_CHECKOUT
|
||||
mkdir -p $TEST_REPO_PATH
|
||||
|
||||
tar -xf $TEST_REPO_ARCHIVE -C $TEST_REPO_PATH
|
||||
|
||||
RAD_HOME=$PALM_RAD_HOME rad auth --init --name palm --passphrase $PASSPHRASE
|
||||
|
||||
docker run \
|
||||
--detach \
|
||||
--init \
|
||||
--publish 8778:8778 \
|
||||
--rm \
|
||||
--env RAD_HOME=/app/radicle \
|
||||
--name radicle-git-server-test \
|
||||
--volume $PALM_RAD_HOME:/app/radicle \
|
||||
"gcr.io/radicle-services/git-server:$REV" \
|
||||
--passphrase $PASSPHRASE \
|
||||
--allow-unauthorized-keys
|
||||
|
||||
# git-server takes a while to copy commit hooks to the monorepo
|
||||
sleep 10
|
||||
|
||||
GIT_AUTHOR_NAME="Alice Liddell"
|
||||
GIT_AUTHOR_EMAIL="alice@radicle.xyz"
|
||||
GIT_COMMITTER_NAME="Alice Liddell"
|
||||
GIT_COMMITTER_EMAIL="alice@radicle.xyz"
|
||||
|
||||
RAD_HOME=$ALICE_RAD_HOME rad auth --init --name alice --passphrase $PASSPHRASE
|
||||
|
||||
cd $ALICE_CHECKOUT
|
||||
|
||||
git clone $TEST_REPO_PATH
|
||||
cd $TEST_REPO_NAME
|
||||
git checkout feature/branch
|
||||
git checkout orphaned-branch
|
||||
git checkout main
|
||||
|
||||
RAD_HOME=$ALICE_RAD_HOME rad init --name "source-browsing" --description "Git repository for source browsing tests" --default-branch "main" --no-confirm
|
||||
RAD_HOME=$ALICE_RAD_HOME rad push --seed 0.0.0.0:8778 --all --sync
|
||||
PROJECT_URN=$(rad .)
|
||||
|
||||
|
||||
GIT_AUTHOR_NAME="Bob Belcher"
|
||||
GIT_AUTHOR_EMAIL="bob@radicle.xyz"
|
||||
GIT_COMMITTER_NAME="Bob Belcher"
|
||||
GIT_COMMITTER_EMAIL="bob@radicle.xyz"
|
||||
|
||||
RAD_HOME=$BOB_RAD_HOME rad auth --init --name bob --passphrase $PASSPHRASE
|
||||
|
||||
cd $BOB_CHECKOUT
|
||||
RAD_HOME=$BOB_RAD_HOME rad clone $PROJECT_URN --seed 0.0.0.0:8778 --no-confirm
|
||||
|
||||
cd $TEST_REPO_NAME
|
||||
echo "Updated readme" > README.md
|
||||
git add README.md
|
||||
git commit --message "Update readme" --date "Mon Nov 21 14:00 2022 +0100"
|
||||
RAD_HOME=$BOB_RAD_HOME rad push --seed 0.0.0.0:8778
|
||||
RAD_HOME=$BOB_RAD_HOME rad sync --seed 0.0.0.0:8778 --self
|
||||
RAD_HOME=$BOB_RAD_HOME rad sync --seed 0.0.0.0:8778
|
||||
|
||||
cd $BASE_PATH
|
||||
tar -cjf palm.tar.bz2 --exclude "post-receive" --exclude "pre-receive" -C $PALM_RAD_HOME .
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
REV=40bdc662b2d48cbfaa87182b21457ef8f861b04a
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
FIXTURE=$REPO_ROOT/tests/fixtures/seeds/palm.tar.bz2
|
||||
WORKSPACE=$REPO_ROOT/tests/tmp/palm
|
||||
PASSPHRASE=asdf
|
||||
CONTAINER_NAME=radicle-http-api-with-fixtures
|
||||
HTTP_API_BINARY=radicle-http-api
|
||||
|
||||
show_usage()
|
||||
{
|
||||
echo
|
||||
echo "Starts a http-api backend with test fixtures."
|
||||
echo
|
||||
echo "USAGE:"
|
||||
echo " run-http-api-with-fixtures [-b|d|h|n]"
|
||||
echo
|
||||
echo "OPTIONS:"
|
||||
echo " -b --binary Use a ${HTTP_API_BINARY} binary that is in PATH to avoid using Docker."
|
||||
echo " -d --detach Daemonize the docker process."
|
||||
echo " -h --help Print this Help."
|
||||
echo " -n --non-interactive Run in non-interactive mode, no user prompts."
|
||||
echo
|
||||
}
|
||||
|
||||
prompt_workspace_removal()
|
||||
{
|
||||
echo "This will irrevocably destroy the following directories:"
|
||||
echo
|
||||
echo $WORKSPACE
|
||||
echo
|
||||
|
||||
read -r -p "Are you sure you want to continue? [yes/no]: " confirm
|
||||
case "$confirm" in
|
||||
[yY][eE][sS] )
|
||||
rm -rf $WORKSPACE
|
||||
echo "Done"
|
||||
;;
|
||||
* )
|
||||
echo "Ok, I won't touch your data."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
prepare_workspace()
|
||||
{
|
||||
echo
|
||||
echo "Unpacking fixture $FIXTURE"
|
||||
mkdir -p $WORKSPACE
|
||||
tar -xf $FIXTURE -C $WORKSPACE
|
||||
}
|
||||
|
||||
run_docker() {
|
||||
echo "Starting docker at container $CONTAINER_NAME at $REV"
|
||||
echo " http-api --root $WORKSPACE --passphrase $PASSPHRASE"
|
||||
echo
|
||||
|
||||
exec docker run \
|
||||
--init \
|
||||
--publish 8777:8777 \
|
||||
--rm \
|
||||
--name $CONTAINER_NAME \
|
||||
--volume $WORKSPACE:/app/radicle \
|
||||
"$@" \
|
||||
"gcr.io/radicle-services/http-api:$REV" \
|
||||
--passphrase $PASSPHRASE
|
||||
}
|
||||
|
||||
run_binary() {
|
||||
if ! [ -x "$(command -v $HTTP_API_BINARY)" ]; then
|
||||
echo
|
||||
echo "Couldn't find the $HTTP_API_BINARY binary in your PATH."
|
||||
echo "You can compile it from source:"
|
||||
echo " 👉 https://github.com/radicle-dev/radicle-client-services/tree/${REV}/http-api"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Starting $HTTP_API_BINARY"
|
||||
echo " $HTTP_API_BINARY --listen 0.0.0.0:8777 --root ${WORKSPACE} --passphrase $PASSPHRASE"
|
||||
echo
|
||||
|
||||
$HTTP_API_BINARY --listen 0.0.0.0:8777 --root $WORKSPACE --passphrase $PASSPHRASE
|
||||
}
|
||||
|
||||
BINADY=false
|
||||
NON_INTERACTIVE=false
|
||||
DETACH=false
|
||||
|
||||
while [ $# -ne 0 ]; do
|
||||
case "$1" in
|
||||
--binary|-b)
|
||||
BINARY=true
|
||||
;;
|
||||
--detach|-d)
|
||||
DETACH=true
|
||||
;;
|
||||
--non-interactive|-n)
|
||||
NON_INTERACTIVE=true
|
||||
;;
|
||||
*)
|
||||
show_usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
if [ "$NON_INTERACTIVE" = true ]; then
|
||||
rm -rf $WORKSPACE
|
||||
prepare_workspace
|
||||
else
|
||||
prompt_workspace_removal
|
||||
prepare_workspace
|
||||
fi
|
||||
|
||||
|
||||
if [ "$BINARY" = true ]; then
|
||||
run_binary
|
||||
else
|
||||
if [ "$DETACH" = true ]; then
|
||||
run_docker --detach
|
||||
else
|
||||
run_docker
|
||||
fi
|
||||
fi
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
npm run test:unit
|
||||
npm run test:components
|
||||
+7
-5
@@ -20,12 +20,14 @@
|
||||
|
||||
initialize();
|
||||
|
||||
const plausible = Plausible({
|
||||
domain: "app.radicle.xyz",
|
||||
hashMode: Boolean(process.env.hashRouting),
|
||||
});
|
||||
if (!window.VITEST && !window.PLAYWRIGHT && import.meta.env.PROD) {
|
||||
const plausible = Plausible({
|
||||
domain: "app.radicle.xyz",
|
||||
hashMode: window.HASH_ROUTING,
|
||||
});
|
||||
|
||||
plausible.enableAutoPageviews();
|
||||
plausible.enableAutoPageviews();
|
||||
}
|
||||
|
||||
const loadWallet = getWallet().then(async wallet => {
|
||||
if ($state.connection === Connection.Connected) {
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import BlockTimer from "./BlockTimer.svelte";
|
||||
|
||||
describe("BlockTimer", () => {
|
||||
describe("when latestBlock === startBlock", () => {
|
||||
it("shows 0% progress", () => {
|
||||
cy.mount(BlockTimer, {
|
||||
props: {
|
||||
latestBlock: 1,
|
||||
startBlock: 1,
|
||||
duration: 3,
|
||||
},
|
||||
});
|
||||
|
||||
cy.get(".progress-bar").should("have.attr", "style", "width: 0%;");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when latestBlock < duration + startBlock", () => {
|
||||
it("shows 33% progress", () => {
|
||||
cy.mount(BlockTimer, {
|
||||
props: {
|
||||
latestBlock: 2,
|
||||
startBlock: 1,
|
||||
duration: 3,
|
||||
},
|
||||
});
|
||||
|
||||
cy.get(".progress-bar").should("have.attr", "style", "width: 33%;");
|
||||
});
|
||||
|
||||
it("shows 66% progress", () => {
|
||||
cy.mount(BlockTimer, {
|
||||
props: {
|
||||
latestBlock: 3,
|
||||
startBlock: 1,
|
||||
duration: 3,
|
||||
},
|
||||
});
|
||||
|
||||
cy.get(".progress-bar").should("have.attr", "style", "width: 66%;");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when latestBlock === duration + startBlock", () => {
|
||||
it("shows 100% progress", () => {
|
||||
cy.mount(BlockTimer, {
|
||||
props: {
|
||||
latestBlock: 4,
|
||||
startBlock: 1,
|
||||
duration: 3,
|
||||
},
|
||||
});
|
||||
|
||||
cy.get(".progress-bar").should("have.attr", "style", "width: 100%;");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when latestBlock > duration + startBlock", () => {
|
||||
it("shows 100% progress", () => {
|
||||
cy.mount(BlockTimer, {
|
||||
props: {
|
||||
latestBlock: 6,
|
||||
startBlock: 1,
|
||||
duration: 3,
|
||||
},
|
||||
});
|
||||
|
||||
cy.get(".progress-bar").should("have.attr", "style", "width: 100%;");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import ErrorModal from "./ErrorModal.svelte";
|
||||
import { Failure } from "@app/error";
|
||||
|
||||
describe("Error", () => {
|
||||
it("should show passed in props", () => {
|
||||
cy.mount(ErrorModal, {
|
||||
props: {
|
||||
subtitle: "Subtitle of Modal",
|
||||
error: {
|
||||
type: Failure.InsufficientBalance,
|
||||
txHash:
|
||||
"0x8b678e51f970c5307bf45a8bcea373b597f9acbcea5c5ba784a1d383361a89d1",
|
||||
message: "Not enough RAD",
|
||||
},
|
||||
},
|
||||
});
|
||||
cy.get("body").contains("Error").should("be.visible");
|
||||
cy.get("body").contains("Subtitle of Modal").should("be.visible");
|
||||
cy.get("body").contains("Not enough RAD").should("be.visible");
|
||||
cy.get("button").contains("Back").should("be.visible");
|
||||
});
|
||||
|
||||
it("should show custom error message", () => {
|
||||
cy.mount(ErrorModal, {
|
||||
props: {
|
||||
subtitle: "Subtitle of Modal",
|
||||
message: "Error message to check for",
|
||||
},
|
||||
});
|
||||
cy.get("body").contains("Error message to check for").should("be.visible");
|
||||
});
|
||||
|
||||
it("should change button label to Close when floating", () => {
|
||||
cy.mount(ErrorModal, {
|
||||
props: {
|
||||
title: "Title of Modal",
|
||||
subtitle: "Subtitle of Modal",
|
||||
message: "Error message to check for",
|
||||
floating: true,
|
||||
},
|
||||
});
|
||||
cy.get("button").contains("Close").should("be.visible");
|
||||
});
|
||||
});
|
||||
+6
-2
@@ -143,6 +143,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
color: var(--color-foreground);
|
||||
}
|
||||
.toggle:hover {
|
||||
background-color: var(--color-foreground);
|
||||
@@ -233,8 +235,10 @@
|
||||
</span>
|
||||
{/if}
|
||||
<Floating>
|
||||
<div class="toggle" slot="toggle">
|
||||
<Icon name="gear" />
|
||||
<div slot="toggle">
|
||||
<button class="toggle" name="Settings">
|
||||
<Icon name="gear" />
|
||||
</button>
|
||||
</div>
|
||||
<SettingsDropdown slot="modal" />
|
||||
</Floating>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import NotFound from "./NotFound.svelte";
|
||||
|
||||
describe("NotFound", () => {
|
||||
it("shows passed props correctly", () => {
|
||||
cy.mount(NotFound, {
|
||||
props: {
|
||||
title: "nakamoto",
|
||||
subtitle: "Sorry, the requested project was not found.",
|
||||
},
|
||||
});
|
||||
cy.get("body").contains("nakamoto").should("be.visible");
|
||||
cy.get("body")
|
||||
.contains("Sorry, the requested project was not found.")
|
||||
.should("be.visible");
|
||||
cy.get("button").contains("Back").should("be.visible");
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
<script lang="ts" strictEvents>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import Icon from "@app/Icon.svelte";
|
||||
import config from "@app/config.json";
|
||||
import { config } from "@app/config";
|
||||
|
||||
const showReactions = false;
|
||||
|
||||
|
||||
+5
-3
@@ -7,7 +7,7 @@
|
||||
import * as utils from "@app/utils";
|
||||
import { Profile } from "@app/profile";
|
||||
import { Project } from "@app/project";
|
||||
import config from "@app/config.json";
|
||||
import { config } from "@app/config";
|
||||
|
||||
export interface ProjectsAndProfiles {
|
||||
projects: { info: ProjectInfo; seed: Host }[];
|
||||
@@ -46,6 +46,8 @@
|
||||
seedHost: projects[0].seed.host,
|
||||
id: query,
|
||||
};
|
||||
} else if (projects.length === 0) {
|
||||
return { type: "nothing" };
|
||||
} else {
|
||||
return {
|
||||
type: "projectsAndProfiles",
|
||||
@@ -203,7 +205,7 @@
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.search {
|
||||
.search-bar {
|
||||
display: flex;
|
||||
}
|
||||
.shaking {
|
||||
@@ -228,7 +230,7 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="search" class:shaking>
|
||||
<div class="search-bar" class:shaking>
|
||||
<TextInput
|
||||
variant="dashed"
|
||||
valid={input !== ""}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import { Seed } from "@app/base/seeds/Seed";
|
||||
|
||||
import SeedAddress from "./SeedAddress.svelte";
|
||||
|
||||
describe("SeedAddress", () => {
|
||||
it("shows the seed emoji and seed host", () => {
|
||||
const seed = new Seed({
|
||||
host: "seed.cloudhead.io",
|
||||
id: "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
|
||||
});
|
||||
|
||||
cy.mount(SeedAddress, {
|
||||
props: {
|
||||
seed,
|
||||
port: 8776,
|
||||
},
|
||||
});
|
||||
cy.get("span.seed-icon img").should("have.attr", "alt", "🌱");
|
||||
cy.get("span.seed-icon img").should(
|
||||
"have.attr",
|
||||
"src",
|
||||
"/twemoji/1f331.svg",
|
||||
);
|
||||
cy.contains("seed.cloudhead.io").should("be.visible");
|
||||
});
|
||||
|
||||
it("shows the full seed id", () => {
|
||||
const seed = new Seed({
|
||||
host: "seed.cloudhead.io",
|
||||
id: "hydkkkf5ksbe5fuszdhpqhytu3q36gwagj874wxwpo5a8ti8coygh1",
|
||||
});
|
||||
cy.mount(SeedAddress, {
|
||||
props: {
|
||||
seed,
|
||||
port: 8776,
|
||||
full: true,
|
||||
},
|
||||
});
|
||||
cy.get("span.seed-icon img").should("have.attr", "alt", "🌱");
|
||||
cy.get("span.seed-icon img").should(
|
||||
"have.attr",
|
||||
"src",
|
||||
"/twemoji/1f331.svg",
|
||||
);
|
||||
cy.get("body")
|
||||
.contains("hydkkk…coygh1@seed.cloudhead.io")
|
||||
.should("be.visible");
|
||||
cy.get("body").contains(":8776").should("be.visible");
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
import Loading from "@app/Loading.svelte";
|
||||
import Message from "@app/Message.svelte";
|
||||
import Widget from "@app/base/projects/Widget.svelte";
|
||||
import config from "@app/config.json";
|
||||
import { config } from "@app/config";
|
||||
import { Project } from "@app/project";
|
||||
import { setOpenGraphMetaTag, twemoji } from "@app/utils";
|
||||
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
import type { ProjectInfo } from "@app/project";
|
||||
|
||||
import BranchSelector from "./BranchSelector.svelte";
|
||||
|
||||
const project: ProjectInfo = {
|
||||
head: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||
urn: "rad:git:hnrkqdpm9ub19oc8dccx44echy76hzfsezyio",
|
||||
name: "nakamoto",
|
||||
description: "Privacy-preserving Bitcoin light-client implementation in Rust",
|
||||
defaultBranch: "master",
|
||||
remotes: ["rad:git:hnrkqdpm9ub19oc8dccx44echy76hzfsezyio"],
|
||||
delegates: [
|
||||
{
|
||||
type: "direct",
|
||||
id: "hyn9diwfnytahjq8u3iw63h9jte1ydcatxax3saymwdxqu1zo645pe",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
project,
|
||||
branches: { master: "e678629cd37c770c640a2cd997fc76303c815772" },
|
||||
revision: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||
};
|
||||
|
||||
describe("Logic", () => {
|
||||
it("should show defaultBranch label and head commit if revision === head", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: defaultProps,
|
||||
});
|
||||
cy.get("div.stat.branch")
|
||||
.should("be.visible")
|
||||
.should("have.text", "master");
|
||||
cy.get("div.hash.layout-mobile")
|
||||
.should("be.visible")
|
||||
.should("have.text", "e678629");
|
||||
});
|
||||
it("if project.head is null we should get the head from branches", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
project: {
|
||||
...project,
|
||||
head: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
cy.get("div.stat.branch")
|
||||
.should("be.visible")
|
||||
.should("have.text", "master");
|
||||
cy.get("div.hash.layout-mobile")
|
||||
.should("be.visible")
|
||||
.should("have.text", "e678629");
|
||||
});
|
||||
|
||||
it("should show the branch dropdown if branches available", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
branches: {
|
||||
master: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||
"feature-branch": "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5",
|
||||
xyz: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
|
||||
},
|
||||
},
|
||||
});
|
||||
cy.get("div.commit div.stat.branch").click();
|
||||
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("should show feature-branch label and head commit, if branch label is passed as revision", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
branches: {
|
||||
master: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||
"feature-branch": "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5",
|
||||
xyz: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
|
||||
},
|
||||
revision: "feature-branch",
|
||||
},
|
||||
});
|
||||
cy.get("div.stat.branch")
|
||||
.should("be.visible")
|
||||
.should("have.text", "feature-branch");
|
||||
cy.get("div.hash.layout-mobile")
|
||||
.should("be.visible")
|
||||
.should("have.text", "29e8b7b");
|
||||
});
|
||||
|
||||
it("should show only commit if no branchLabel nor branches are available", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
revision: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
|
||||
branches: {},
|
||||
},
|
||||
});
|
||||
cy.get("div.hash.layout-mobile")
|
||||
.should("be.visible")
|
||||
.should("have.text", "debf82e");
|
||||
cy.viewport("macbook-13");
|
||||
cy.get("div.hash.layout-desktop")
|
||||
.should("be.visible")
|
||||
.should("have.text", "debf82ef3623ec11751a993bda85bac2ff1c6f00");
|
||||
});
|
||||
|
||||
it("should show only commit if branches are available but no branchLabel", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
revision: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
|
||||
},
|
||||
});
|
||||
cy.get("div.hash.layout-mobile")
|
||||
.should("be.visible")
|
||||
.should("have.text", "debf82e");
|
||||
cy.viewport("macbook-13");
|
||||
cy.get("div.hash.layout-desktop")
|
||||
.should("be.visible")
|
||||
.should("have.text", "debf82ef3623ec11751a993bda85bac2ff1c6f00");
|
||||
});
|
||||
|
||||
it("should show defaultBranch label if revision === head", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
revision: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||
branches: {},
|
||||
},
|
||||
});
|
||||
cy.get("div.stat.branch.not-allowed")
|
||||
.should("be.visible")
|
||||
.should("have.text", "master");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Layout", () => {
|
||||
it("should show shortened commit when on mobile, and full hash when on desktop", () => {
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
revision: "e678629cd37c770c640a2cd997fc76303c815772",
|
||||
},
|
||||
});
|
||||
cy.viewport("iphone-x");
|
||||
cy.get("div.hash.layout-mobile").should("be.visible");
|
||||
cy.get("div.hash.layout-desktop").should("not.be.visible");
|
||||
cy.viewport("macbook-15");
|
||||
cy.get("div.hash.layout-mobile").should("not.be.visible");
|
||||
cy.get("div.hash.layout-desktop").should("be.visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", () => {
|
||||
it("should dispatch a 'branchChanged' event on click", () => {
|
||||
const branchChangedSpy = cy.spy().as("branchChangedSpy");
|
||||
|
||||
cy.mount(BranchSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
revision: "feature-branch",
|
||||
branches: {
|
||||
"feature-branch": "29e8b7b0f3019b8e8a6d9bfb0964ee78f4ff12f5",
|
||||
xyz: "debf82ef3623ec11751a993bda85bac2ff1c6f00",
|
||||
},
|
||||
},
|
||||
}).then(({ component }) => {
|
||||
component.$on("branchChanged", branchChangedSpy);
|
||||
});
|
||||
|
||||
cy.get("body").contains("feature-branch").click();
|
||||
cy.get("body").contains("xyz").click();
|
||||
cy.get("@branchChangedSpy").should("have.been.called");
|
||||
});
|
||||
});
|
||||
@@ -72,13 +72,14 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="commit" title="Switch branches">
|
||||
<div class="commit" title="Current branch">
|
||||
<!-- Check for branches listing feature -->
|
||||
{#if branchList.length > 0}
|
||||
{#if branchLabel}
|
||||
<Floating disabled={!showSelector}>
|
||||
<div
|
||||
slot="toggle"
|
||||
title="Change branch"
|
||||
class="stat branch"
|
||||
class:not-allowed={!showSelector}>
|
||||
{branchLabel}
|
||||
|
||||
@@ -35,7 +35,10 @@
|
||||
// Whether the mobile file tree is visible.
|
||||
let mobileFileTree = false;
|
||||
|
||||
const loadBlob = async (path: string, theme: Theme): Promise<proj.Blob> => {
|
||||
const loadBlob = async (
|
||||
path: string,
|
||||
theme: Theme,
|
||||
): Promise<proj.Blob | undefined> => {
|
||||
if (
|
||||
state.status === Status.Loaded &&
|
||||
state.path === path &&
|
||||
@@ -57,9 +60,12 @@
|
||||
);
|
||||
|
||||
state = { status: Status.Loading, path };
|
||||
state = { status: Status.Loaded, path, blob: await promise, theme };
|
||||
|
||||
return state.blob;
|
||||
try {
|
||||
state = { status: Status.Loaded, path, blob: await promise, theme };
|
||||
return state.blob;
|
||||
} catch (err) {
|
||||
console.warn("Could not load blob.");
|
||||
}
|
||||
};
|
||||
|
||||
// Get an image blob based on a relative path.
|
||||
@@ -206,7 +212,9 @@
|
||||
{#await getBlob}
|
||||
<Loading small center />
|
||||
{:then blob}
|
||||
<Blob {line} {blob} {getImage} {activeRoute} />
|
||||
{#if blob}
|
||||
<Blob {line} {blob} {getImage} {activeRoute} />
|
||||
{/if}
|
||||
{:catch}
|
||||
<Placeholder emoji="🍂">
|
||||
<span slot="title">
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import PeerSelector from "./PeerSelector.svelte";
|
||||
|
||||
const defaultProps = {
|
||||
peer: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
peers: [
|
||||
{
|
||||
id: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
person: { name: "sebastinez" },
|
||||
delegate: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe("Logic", () => {
|
||||
it("show delegate name and badge", () => {
|
||||
cy.mount(PeerSelector, {
|
||||
props: defaultProps,
|
||||
});
|
||||
cy.get("span.peer-id").should("have.text", "sebastinez");
|
||||
cy.get("span.badge.primary").should("have.text", "delegate");
|
||||
});
|
||||
|
||||
it("show peer id with badge if no name available", () => {
|
||||
cy.mount(PeerSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
peers: [
|
||||
{
|
||||
id: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
delegate: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
cy.get("span.peer-id").should("have.text", "hyyg55…p7ofue");
|
||||
cy.get("span.badge.primary").should("have.text", "delegate");
|
||||
});
|
||||
|
||||
it("show only peer id if no additional data available", () => {
|
||||
cy.mount(PeerSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
peers: [
|
||||
{
|
||||
id: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
delegate: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
cy.get("span.peer-id").should("have.text", "hyyg55…p7ofue");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Layout", () => {
|
||||
it("should highlight the current peer", () => {
|
||||
cy.mount(PeerSelector, {
|
||||
props: { ...defaultProps },
|
||||
});
|
||||
cy.get("div.selector").click();
|
||||
cy.get("div.dropdown-item").should("have.class", "selected");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", () => {
|
||||
it("dispatch peerChanged event if clicking on a peer", () => {
|
||||
const peerChangedSpy = cy.spy().as("peerChangedSpy");
|
||||
|
||||
cy.mount(PeerSelector, {
|
||||
props: {
|
||||
...defaultProps,
|
||||
peers: [
|
||||
{
|
||||
id: "hyy841u4phudmr8s5rg1jjwd1ct7x7438wmjwtsm464y8uyxyhyi6c",
|
||||
person: { name: "cloudhead" },
|
||||
delegate: true,
|
||||
},
|
||||
{
|
||||
id: "hyyg555wwkkutaysg6yr67qnu5d5ji54iur3n5uzzszndh8dp7ofue",
|
||||
person: { name: "sebastinez" },
|
||||
delegate: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}).then(({ component }) => {
|
||||
component.$on("peerChanged", peerChangedSpy);
|
||||
});
|
||||
|
||||
cy.get("body").contains("sebastinez").click();
|
||||
cy.get("body").contains("cloudhead").click();
|
||||
cy.get("@peerChangedSpy").should("have.been.called");
|
||||
});
|
||||
});
|
||||
@@ -85,7 +85,7 @@
|
||||
</style>
|
||||
|
||||
<Floating>
|
||||
<div slot="toggle" class="selector" title="Switch peers">
|
||||
<div slot="toggle" class="selector" title="Change peer">
|
||||
<div class="stat peer" class:not-allowed={!peers}>
|
||||
<Icon name="fork" />
|
||||
{#if meta}
|
||||
|
||||
@@ -48,13 +48,16 @@
|
||||
activeRoute.params.route,
|
||||
project.branches,
|
||||
);
|
||||
router.updateProjectRoute({
|
||||
revision,
|
||||
path,
|
||||
line: activeRoute.params.line,
|
||||
hash: activeRoute.params.hash,
|
||||
route: undefined,
|
||||
});
|
||||
router.updateProjectRoute(
|
||||
{
|
||||
revision,
|
||||
path,
|
||||
line: activeRoute.params.line,
|
||||
hash: activeRoute.params.hash,
|
||||
route: undefined,
|
||||
},
|
||||
{ replace: true },
|
||||
);
|
||||
}
|
||||
if (!activeRoute.params.revision) {
|
||||
// We need a revision to fetch `getRoot`.
|
||||
|
||||
@@ -60,10 +60,6 @@ export const state = writable<Connection>({ connection: State.Connecting });
|
||||
|
||||
window.registrarState = state;
|
||||
|
||||
state.subscribe((s: Connection) => {
|
||||
console.debug("register.state", s);
|
||||
});
|
||||
|
||||
export async function getRegistration(
|
||||
name: string,
|
||||
wallet: Wallet,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import configJson from "@app/config.json";
|
||||
|
||||
export interface Config {
|
||||
walletConnect: { bridge: string };
|
||||
reactions: string[];
|
||||
seeds: {
|
||||
pinned: { host: string; emoji: string }[];
|
||||
};
|
||||
projects: {
|
||||
pinned: {
|
||||
name: string;
|
||||
urn: string;
|
||||
seed: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
function getConfig(): Config {
|
||||
if (window.VITEST) {
|
||||
return {
|
||||
walletConnect: { bridge: "" },
|
||||
reactions: [],
|
||||
seeds: {
|
||||
pinned: [],
|
||||
},
|
||||
projects: { pinned: [] },
|
||||
};
|
||||
} else if (window.PLAYWRIGHT) {
|
||||
return window.APP_CONFIG;
|
||||
} else {
|
||||
// In dev and production environments we use data from config.json.
|
||||
return configJson;
|
||||
}
|
||||
}
|
||||
|
||||
export const config = getConfig();
|
||||
@@ -0,0 +1,8 @@
|
||||
import * as FakeTimers from "@sinonjs/fake-timers";
|
||||
|
||||
if (typeof window.initializeTestStubs === "function") {
|
||||
window.e2eTestStubs = {
|
||||
FakeTimers: FakeTimers,
|
||||
};
|
||||
window.initializeTestStubs();
|
||||
}
|
||||
Vendored
+32
@@ -0,0 +1,32 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { Config } from "@app/config";
|
||||
import type { FakeTimers } from "@sinonjs/fake-timers";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
// Defined in vite.config.ts and are available in all environments except
|
||||
// production.
|
||||
VITEST: boolean;
|
||||
PLAYWRIGHT: boolean;
|
||||
HASH_ROUTING: boolean;
|
||||
|
||||
// APP_CONFIG is set from within Playwright tests at runtime.
|
||||
// To better understand how it works together, have a look at:
|
||||
// tests/support/fixtures.ts
|
||||
// src/config.ts
|
||||
APP_CONFIG: Config;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
initializeTestStubs: Function;
|
||||
e2eTestStubs: {
|
||||
FakeTimers: FakeTimers;
|
||||
};
|
||||
|
||||
// Used in
|
||||
// src/session.ts
|
||||
// src/wallet.ts
|
||||
ethereum: any;
|
||||
registrarState: any;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
+1
-8
@@ -1,15 +1,8 @@
|
||||
if (window.PLAYWRIGHT) import("./e2eTestStubs");
|
||||
import App from "./App.svelte";
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
});
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
hashRouting: number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default app;
|
||||
|
||||
+14
-7
@@ -16,13 +16,15 @@ export const activeRouteStore: Readable<Route> = derived(
|
||||
},
|
||||
);
|
||||
|
||||
export const base = process.env.hashRouting ? "./" : "/";
|
||||
export const base = window.HASH_ROUTING ? "./" : "/";
|
||||
|
||||
// Gets triggered when clicking on an anchor hash tag e.g. <a href="#header"/>
|
||||
// Allows the jump to a anchor hash
|
||||
window.addEventListener("hashchange", e => {
|
||||
const url = new URL(e.newURL);
|
||||
updateProjectRoute({ hash: url.hash.substring(1) });
|
||||
const route = pathToRoute(e.newURL);
|
||||
if (route?.resource === "projects" && route.params.hash) {
|
||||
updateProjectRoute({ hash: route.params.hash });
|
||||
}
|
||||
});
|
||||
|
||||
// Replaces history on any user interaction with forward and backwards buttons
|
||||
@@ -62,12 +64,17 @@ export function projectLinkHref(
|
||||
|
||||
export function updateProjectRoute(
|
||||
projectRouteParams: Partial<ProjectsParams>,
|
||||
opts: { replace: boolean } = { replace: false },
|
||||
) {
|
||||
const activeRoute = get(activeRouteStore);
|
||||
|
||||
if (activeRoute.resource === "projects") {
|
||||
const updatedRoute = createProjectRoute(activeRoute, projectRouteParams);
|
||||
push(updatedRoute);
|
||||
if (opts.replace) {
|
||||
replace(updatedRoute);
|
||||
} else {
|
||||
push(updatedRoute);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
"Don't use project specific navigation outside of project views",
|
||||
@@ -82,7 +89,7 @@ export const push = (newRoute: Route): void => {
|
||||
// one subsequent pop() anyway.
|
||||
historyStore.set([...history, newRoute].slice(-10));
|
||||
|
||||
const path = process.env.hashRouting
|
||||
const path = window.HASH_ROUTING
|
||||
? "#" + routeToPath(newRoute)
|
||||
: routeToPath(newRoute);
|
||||
|
||||
@@ -101,7 +108,7 @@ export const pop = (): void => {
|
||||
export function replace(newRoute: Route): void {
|
||||
historyStore.set([newRoute]);
|
||||
|
||||
const path = process.env.hashRouting
|
||||
const path = window.HASH_ROUTING
|
||||
? "#" + routeToPath(newRoute)
|
||||
: routeToPath(newRoute);
|
||||
|
||||
@@ -127,7 +134,7 @@ function pathToRoute(path: string): Route | null {
|
||||
}
|
||||
|
||||
const url = new URL(path, window.origin);
|
||||
const segments = process.env.hashRouting
|
||||
const segments = window.HASH_ROUTING
|
||||
? url.hash.substring(2).split("#")[0].split("/") // Try to remove any additional hashes at the end of the URL.
|
||||
: url.pathname.substring(1).split("/");
|
||||
|
||||
|
||||
@@ -404,10 +404,6 @@ export async function connectSeed(seedSession: {
|
||||
state.connectSeed(seedSession);
|
||||
}
|
||||
|
||||
state.subscribe(s => {
|
||||
console.debug("session.state", s);
|
||||
});
|
||||
|
||||
export async function approveSpender(
|
||||
spender: string,
|
||||
amount: BigNumber,
|
||||
|
||||
+1
-1
@@ -3,7 +3,6 @@ import type { Wallet } from "@app/wallet";
|
||||
import type { marked } from "marked";
|
||||
|
||||
import * as cache from "@app/cache";
|
||||
import config from "@app/config.json";
|
||||
import emojis from "@app/emojis";
|
||||
import katex from "katex";
|
||||
import md5 from "md5";
|
||||
@@ -12,6 +11,7 @@ import { BigNumber } from "ethers";
|
||||
import { ProfileType } from "@app/profile";
|
||||
import { assert } from "@app/error";
|
||||
import { base } from "@app/router";
|
||||
import { config } from "@app/config";
|
||||
import { ethers } from "ethers";
|
||||
import { getAddress, getResolver } from "@app/base/registrations/registrar";
|
||||
import {
|
||||
|
||||
+10
-12
@@ -10,7 +10,7 @@ import { capitalize } from "@app/utils";
|
||||
import ethereumContractAbis from "@app/ethereum/contractAbis.json";
|
||||
import homestead from "@app/ethereum/networks/homestead.json";
|
||||
import goerli from "@app/ethereum/networks/goerli.json";
|
||||
import config from "@app/config.json";
|
||||
import { config } from "@app/config";
|
||||
|
||||
interface NetworkConfig {
|
||||
name: string;
|
||||
@@ -29,15 +29,6 @@ interface NetworkConfig {
|
||||
alchemy: { key: string };
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Cypress: any;
|
||||
ethereum: any;
|
||||
registrarState: any;
|
||||
}
|
||||
}
|
||||
|
||||
export type WalletConnectState =
|
||||
| { state: "close" }
|
||||
| { state: "open"; uri: string; onClose: any };
|
||||
@@ -200,12 +191,19 @@ function getProvider(
|
||||
): ethers.providers.JsonRpcProvider {
|
||||
if (metamask) {
|
||||
return metamask;
|
||||
} else if (import.meta.env.PROD) {
|
||||
} else if (
|
||||
import.meta.env.PROD &&
|
||||
window.location.host !== "localhost:4173"
|
||||
) {
|
||||
return new ethers.providers.AlchemyWebSocketProvider(
|
||||
networkConfig.name,
|
||||
networkConfig.alchemy.key,
|
||||
);
|
||||
} else if (import.meta.env.DEV) {
|
||||
}
|
||||
// Run the production smoke test with the ethers provider,
|
||||
// because we block requests from localhost on Alchemy,
|
||||
// which in turn throws an exception.
|
||||
else if (import.meta.env.DEV || window.location.host === "localhost:4173") {
|
||||
// The ethers defaultProvider doesn't include a `send` method, which breaks the `utils.getTokens` fn.
|
||||
// Since Metamask nor WalletConnect provide an `alchemy_getTokenBalances` nor `alchemy_getTokenMetadata` endpoint,
|
||||
// we can rely on not using `config.provider.send`.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
test("exceptions in production build", async ({ page, browserName }) => {
|
||||
// It's enough to check this once.
|
||||
if (browserName !== "chromium") {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto("/");
|
||||
// Wait for scripts to finish executing, there might be exceptions that
|
||||
// happen after the page has been painted.
|
||||
await page.waitForTimeout(2000);
|
||||
await expect(
|
||||
page.locator(
|
||||
"text=Radicle enables developers to securely collaborate on software over a peer-to-peer network built on Git.",
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
const sourceBrowsingFixture =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o";
|
||||
|
||||
async function expectClipboard(content: string, page: Page) {
|
||||
const clipboardContent = await page.evaluate<string>(
|
||||
"navigator.clipboard.readText()",
|
||||
);
|
||||
expect(clipboardContent).toBe(content);
|
||||
}
|
||||
|
||||
// We explicitly run all clipboard tests withing the context of a single test
|
||||
// so that we don't run into race conditions, because there is no way to isolate
|
||||
// the clipboard in Playwright yet.
|
||||
test("copy to clipboard", async ({ page, browserName, context }) => {
|
||||
// These tests only work in Chromium, because other browsers don't support
|
||||
// changing permissions.
|
||||
if (browserName !== "chromium") {
|
||||
test.skip();
|
||||
}
|
||||
context.grantPermissions(["clipboard-read", "clipboard-write"]);
|
||||
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
// Reset system clipboard to a known state.
|
||||
await page.evaluate<string>("navigator.clipboard.writeText('')");
|
||||
|
||||
// Project URN.
|
||||
{
|
||||
await page.locator(".urn > .clipboard").click();
|
||||
const clipboardContent = await page.evaluate<string>(
|
||||
"navigator.clipboard.readText()",
|
||||
);
|
||||
expect(clipboardContent).toBe(
|
||||
"rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
);
|
||||
}
|
||||
|
||||
// `rad clone` URL.
|
||||
{
|
||||
await page.getByText("Clone").click();
|
||||
await page.locator("text=rad clone rad://0.0.0.0/hnrkgd").hover();
|
||||
await page
|
||||
.locator(".clone-url-wrapper > span")
|
||||
.first()
|
||||
.locator(".clipboard")
|
||||
.click();
|
||||
await expectClipboard(
|
||||
"rad clone rad://0.0.0.0/hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
page,
|
||||
);
|
||||
}
|
||||
|
||||
// `git clone` URL.
|
||||
{
|
||||
await page.getByText("Clone").click();
|
||||
await page.locator("text=https://0.0.0.0/hnrkgd").hover();
|
||||
await page
|
||||
.locator(".clone-url-wrapper > span")
|
||||
.last()
|
||||
.locator(".clipboard")
|
||||
.click();
|
||||
await expectClipboard(
|
||||
"https://0.0.0.0/hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o.git",
|
||||
page,
|
||||
);
|
||||
}
|
||||
|
||||
await page.goto("/seeds/radicle.local");
|
||||
// Seed address.
|
||||
{
|
||||
await page.locator(".clipboard").first().click();
|
||||
await expectClipboard("0.0.0.0", page);
|
||||
|
||||
await page.locator(".clipboard").last().click();
|
||||
await expectClipboard(
|
||||
"hyb6i8oggc3mgra9siy8yuohhtz34r98pcybja97c9o789wpsg6nn4",
|
||||
page,
|
||||
);
|
||||
}
|
||||
|
||||
// Clear the system clipboard contents so developers don't wonder why there's
|
||||
// random stuff in their clipboard after running tests.
|
||||
await page.evaluate<string>("navigator.clipboard.writeText('')");
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
test.use({
|
||||
customAppConfig: true,
|
||||
});
|
||||
|
||||
test("show pinned projects", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.APP_CONFIG = {
|
||||
walletConnect: {
|
||||
bridge: "https://radicle.bridge.walletconnect.org",
|
||||
},
|
||||
reactions: [],
|
||||
seeds: {
|
||||
pinned: [{ host: "0.0.0.0", emoji: "🚀" }],
|
||||
},
|
||||
projects: {
|
||||
pinned: [
|
||||
{
|
||||
name: "source-browsing",
|
||||
urn: "rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
seed: "0.0.0.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
await page.goto("/");
|
||||
await expect(
|
||||
page.locator("text=Explore projects on the Radicle network."),
|
||||
).toBeVisible();
|
||||
|
||||
// Shows pinned project name.
|
||||
await expect(page.locator("text=source-browsing")).toBeVisible();
|
||||
//
|
||||
// Shows pinned project description.
|
||||
await expect(
|
||||
page.locator("text=Git repository for source browsing tests"),
|
||||
).toBeVisible();
|
||||
|
||||
// Shows latest commit.
|
||||
await expect(page.locator("text=530aabd")).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,339 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
const sourceBrowsingFixture =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o";
|
||||
|
||||
async function expectCounts(
|
||||
params: { commits: number; contributors: number },
|
||||
page: Page,
|
||||
) {
|
||||
await expect(page.locator('role=button[name="Commit count"]')).toContainText(
|
||||
`${params.commits} commit(s)`,
|
||||
);
|
||||
await expect(
|
||||
page.locator('role=button[name="Contributor count"]'),
|
||||
).toContainText(`${params.contributors} contributor(s)`);
|
||||
}
|
||||
|
||||
test("navigate to project", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
// Header.
|
||||
{
|
||||
const name = page.locator("text=source-browsing");
|
||||
const urn = page.locator(
|
||||
"text=rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
);
|
||||
const description = page.locator(
|
||||
"text=Git repository for source browsing tests",
|
||||
);
|
||||
|
||||
await expect(name).toBeVisible();
|
||||
await expect(urn).toBeVisible();
|
||||
await expect(description).toBeVisible();
|
||||
}
|
||||
|
||||
// Project menu shows default selected branch and commit and contributor counts.
|
||||
{
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"main 530aabd",
|
||||
);
|
||||
await expectCounts({ commits: 7, contributors: 1 }, page);
|
||||
}
|
||||
|
||||
// Navigate to the project README.md by default.
|
||||
await expect(page.locator(".file-name")).toContainText("README.md");
|
||||
|
||||
// Show a commit teaser.
|
||||
await expect(page.locator("text=dd068e9 Add README.md")).toBeVisible();
|
||||
|
||||
// Show rendered README.md contents.
|
||||
await expect(page.locator("text=Git test repository")).toBeVisible();
|
||||
});
|
||||
|
||||
test("show source tree at specific revision", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
await page
|
||||
.locator(".commit-teaser", { hasText: "335dd6d" })
|
||||
.getByTitle("Browse the repository at this point in the history")
|
||||
.click();
|
||||
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"335dd6dc89b535a4a31e9422c803199bb6b0a09a",
|
||||
);
|
||||
expect(page.locator(".source-tree")).toHaveText("bin/ src/");
|
||||
await expectCounts({ commits: 2, contributors: 1 }, page);
|
||||
});
|
||||
|
||||
test("source file highlighting", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
await page.getByText("src/").click();
|
||||
await page.getByText("true.c").click();
|
||||
|
||||
await expect(page.getByText("return")).toHaveCSS(
|
||||
"color",
|
||||
"rgb(180, 142, 173)",
|
||||
);
|
||||
});
|
||||
|
||||
test("navigate deep file hierarchies", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
const sourceTree = page.locator(".source-tree");
|
||||
|
||||
await sourceTree.getByText("deep/").click();
|
||||
await sourceTree.getByText("directory/").click();
|
||||
await sourceTree.getByText("hierarchy/").click();
|
||||
await sourceTree.getByText("is/").click();
|
||||
await sourceTree.getByText("entirely/").click();
|
||||
await sourceTree.getByText("possible/").click();
|
||||
await sourceTree.getByText("in/").nth(1).click();
|
||||
await sourceTree.getByText("git/").click();
|
||||
await sourceTree.getByText("repositories/").click();
|
||||
await sourceTree.getByText(".gitkeep").click();
|
||||
await expect(
|
||||
page.locator("text=0801ace Add a deeply nested directory tree"),
|
||||
).toBeVisible();
|
||||
|
||||
// After a page reload the tree browser is still expanded and we're still
|
||||
// showing the .gitkeep file.
|
||||
{
|
||||
await page.reload();
|
||||
|
||||
const sourceTree = page.locator(".source-tree");
|
||||
|
||||
await expect(sourceTree.getByText("deep/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("directory/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("hierarchy/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("is/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("entirely/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("possible/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("in/").nth(1)).toBeVisible();
|
||||
await expect(sourceTree.getByText("git/")).toBeVisible();
|
||||
await expect(sourceTree.getByText("repositories/")).toBeVisible();
|
||||
await expect(sourceTree.getByText(".gitkeep")).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.locator("text=0801ace Add a deeply nested directory tree"),
|
||||
).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test("files with special characters in the filename", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
const sourceTree = page.locator(".source-tree");
|
||||
await sourceTree.getByText("special/").click();
|
||||
|
||||
await sourceTree.getByText("+plus+").click();
|
||||
await expect(page.locator(".file-name")).toContainText("+plus");
|
||||
|
||||
await sourceTree.getByText("-dash-").click();
|
||||
await expect(page.locator(".file-name")).toContainText("-dash-");
|
||||
|
||||
await sourceTree.getByText(":colon:").click();
|
||||
await expect(page.locator(".file-name")).toContainText(":colon:");
|
||||
|
||||
await sourceTree.getByText(";semicolon;").click();
|
||||
await expect(page.locator(".file-name")).toContainText(";semicolon;");
|
||||
|
||||
await sourceTree.getByText("@at@").click();
|
||||
await expect(page.locator(".file-name")).toContainText("@at@");
|
||||
|
||||
await sourceTree.getByText("_underscore_").click();
|
||||
await expect(page.locator(".file-name")).toContainText("_underscore_");
|
||||
|
||||
// TODO: fix these errors in `racdicle-client-services/http-api` for the
|
||||
// following edge cases.
|
||||
//
|
||||
// await sourceTree.getByText("back\\slash").click();
|
||||
// await expect(page.locator(".file-name")).toContainText("back\\slash");
|
||||
// await sourceTree.getByText("qs?param1=value?param2=value2#hash").click();
|
||||
// await expect(page.locator(".file-name")).toContainText(
|
||||
// "qs?param1=value?param2=value2#hash",
|
||||
// );
|
||||
|
||||
await sourceTree.getByText("spaces are okay").click();
|
||||
await expect(page.locator(".file-name")).toContainText("spaces are okay");
|
||||
|
||||
await sourceTree.getByText("~tilde~").click();
|
||||
await expect(page.locator(".file-name")).toContainText("~tilde~");
|
||||
|
||||
await sourceTree.getByText("👹👹👹").click();
|
||||
await expect(page.locator(".file-name")).toContainText("👹👹👹");
|
||||
});
|
||||
|
||||
test("binary files", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
await page.getByText("bin/").click();
|
||||
await page.getByText("true").click();
|
||||
|
||||
await expect(page.locator("text=Binary content")).toBeVisible();
|
||||
});
|
||||
|
||||
test("hidden files", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
await page.getByText(".hidden").click();
|
||||
|
||||
await expect(page.locator("text=I'm a hidden file.")).toBeVisible();
|
||||
});
|
||||
|
||||
test("markdown files", async ({ page }) => {
|
||||
await page.goto(`${sourceBrowsingFixture}/tree/main/markdown/cheatsheet.md`);
|
||||
|
||||
await expect(
|
||||
page.locator("text=This is intended as a quick reference and showcase."),
|
||||
).toBeVisible();
|
||||
|
||||
// Switch between raw and rendered modes.
|
||||
{
|
||||
const rawButton = page.locator('role=button[name="Raw"]');
|
||||
|
||||
await rawButton.click();
|
||||
await expect(rawButton).toHaveClass(/active/);
|
||||
await expect(page.locator("text=##### Table of Contents")).toBeVisible();
|
||||
|
||||
await rawButton.click();
|
||||
await expect(rawButton).not.toHaveClass("active");
|
||||
}
|
||||
|
||||
// Internal links go to anchor.
|
||||
{
|
||||
await page.getByRole("link", { name: "YouTube Videos" }).click();
|
||||
await expect(page).toHaveURL(
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/main/markdown/cheatsheet.md#videos",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("peer and branch switching", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
// Alice's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=alice").click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("alice delegate");
|
||||
await expect(
|
||||
page.locator("text=source-browsing / hyn1mj…qx7bun"),
|
||||
).toBeVisible();
|
||||
|
||||
// Default `main` branch.
|
||||
{
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"main 530aabd",
|
||||
);
|
||||
await expectCounts({ commits: 7, contributors: 1 }, page);
|
||||
}
|
||||
|
||||
// Feature branch with a slash in the name.
|
||||
{
|
||||
await page.getByTitle("Change branch").click();
|
||||
await page.locator("text=feature/branch").click();
|
||||
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"feature/branch d6318f7",
|
||||
);
|
||||
await expectCounts({ commits: 10, contributors: 1 }, page);
|
||||
}
|
||||
|
||||
// Branch without a history or files in it.
|
||||
{
|
||||
await page.getByTitle("Change branch").click();
|
||||
await page.locator("text=orphaned-branch").click();
|
||||
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"orphaned-branch af3641c",
|
||||
);
|
||||
await expectCounts({ commits: 1, contributors: 1 }, page);
|
||||
|
||||
await expect(
|
||||
page.locator("text=We couldn't find any files at this revision."),
|
||||
).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the source browser by clicking the project title.
|
||||
{
|
||||
await page.locator("text=source-browsing").click();
|
||||
|
||||
await expect(page.getByTitle("Change peer")).not.toContainText("alice");
|
||||
await expect(page.getByTitle("Change peer")).not.toContainText("bob");
|
||||
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"main 530aabd",
|
||||
);
|
||||
await expect(page.locator("text=Git test repository")).toBeVisible();
|
||||
}
|
||||
|
||||
// Bob's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=bob").click();
|
||||
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("bob");
|
||||
await expect(page.getByTitle("Change peer")).not.toHaveText("delegate");
|
||||
|
||||
// Default `main` branch.
|
||||
{
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"main 0be0f03",
|
||||
);
|
||||
await expectCounts({ commits: 8, contributors: 2 }, page);
|
||||
await expect(page.locator("text=0be0f03 Update readme")).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("clone modal", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
await page.getByText("Clone").click();
|
||||
await expect(
|
||||
page.locator(
|
||||
"text=rad clone rad://0.0.0.0/hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.locator(
|
||||
"text=https://0.0.0.0/hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o.git",
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("only one modal can be open at a time", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=alice hyn1mj").click();
|
||||
|
||||
await page.getByText("Clone").click();
|
||||
await expect(page.locator("text=Code font")).not.toBeVisible();
|
||||
await expect(page.locator("text=Use the Radicle CLI")).toBeVisible();
|
||||
await expect(page.locator("text=bob hyy1k6g")).not.toBeVisible();
|
||||
await expect(page.locator("text=feature/branch")).not.toBeVisible();
|
||||
|
||||
await page.getByTitle("Change branch").click();
|
||||
await expect(page.locator("text=Code font")).not.toBeVisible();
|
||||
await expect(page.locator("text=Use the Radicle CLI")).not.toBeVisible();
|
||||
await expect(page.locator("text=bob hyy1k6g")).not.toBeVisible();
|
||||
await expect(page.locator("text=feature/branch")).toBeVisible();
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await expect(page.locator("text=Code font")).not.toBeVisible();
|
||||
await expect(page.locator("text=Use the Radicle CLI")).not.toBeVisible();
|
||||
await expect(page.locator("text=bob hyy1k6g")).toBeVisible();
|
||||
await expect(page.locator("text=feature/branch")).not.toBeVisible();
|
||||
|
||||
page.locator('button[name="Settings"]').click();
|
||||
await expect(page.locator("text=Code font")).toBeVisible();
|
||||
await expect(page.locator("text=Use the Radicle CLI")).not.toBeVisible();
|
||||
await expect(page.locator("text=bob hyy1k6g")).not.toBeVisible();
|
||||
await expect(page.locator("text=feature/branch")).not.toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
const sourceBrowsingFixture =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o";
|
||||
const modifiedFileFixture = `${sourceBrowsingFixture}/remotes/hyy1k6ggg45pi7ip7ksyn1wt1ob4w5zh1awtg4qu3cxmbh5mws8pj1/commits/0be0f0302269b362be0bfe72aa4843eceaac5e3f`;
|
||||
|
||||
test("navigation from commit list", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=bob hyy1k6").click();
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
await page.locator("text=Update readme").click();
|
||||
await expect(page).toHaveURL(modifiedFileFixture);
|
||||
});
|
||||
|
||||
test("relative timestamps", async ({ page }) => {
|
||||
page.addInitScript(() => {
|
||||
window.initializeTestStubs = () => {
|
||||
window.e2eTestStubs.FakeTimers.install({
|
||||
now: new Date("November 24 2022 12:00:00").valueOf(),
|
||||
shouldClearNativeTimers: true,
|
||||
shouldAdvanceTime: false,
|
||||
});
|
||||
};
|
||||
});
|
||||
await page.goto(modifiedFileFixture);
|
||||
await expect(
|
||||
page.locator(".commit header >> text=bob committed 3 days ago"),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("modified file", async ({ page }) => {
|
||||
await page.goto(modifiedFileFixture);
|
||||
|
||||
// Commit header.
|
||||
{
|
||||
const header = page.locator(".commit header");
|
||||
await expect(header.locator("text=Update readme")).toBeVisible();
|
||||
await expect(header.locator("text=Verified")).toBeVisible();
|
||||
await expect(
|
||||
header.locator("text=0be0f0302269b362be0bfe72aa4843eceaac5e3f"),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
// Diff header.
|
||||
await expect(
|
||||
page.locator("text=1 file(s) changed with 1 addition(s) and 4 deletion(s)"),
|
||||
).toBeVisible();
|
||||
|
||||
// Diff.
|
||||
await expect(page.locator("text=- # Git test repository")).toBeVisible();
|
||||
await expect(page.locator("text=+ Updated readme")).toBeVisible();
|
||||
});
|
||||
|
||||
test("created file", async ({ page }) => {
|
||||
await page.goto(
|
||||
`${sourceBrowsingFixture}/remotes/hyn1mjueopwzrmb18c3zmgg8ei8qunn5wpg76ouymytfqkfxqx7bun/commits/d6318f7f3d9c15b8ac6dd52267c53220d00f0982`,
|
||||
);
|
||||
await expect(
|
||||
page.locator("text=1 file(s) created with 9 addition(s) and 0 deletion(s)"),
|
||||
).toBeVisible();
|
||||
await expect(page.locator("text=subconscious.txt created")).toBeVisible();
|
||||
});
|
||||
|
||||
test("deleted file", async ({ page }) => {
|
||||
await page.goto(
|
||||
`${sourceBrowsingFixture}/remotes/hyn1mjueopwzrmb18c3zmgg8ei8qunn5wpg76ouymytfqkfxqx7bun/commits/cd13c2d9a8a930d64a82b6134b44d1b872e33662`,
|
||||
);
|
||||
await expect(
|
||||
page.locator("text=1 file(s) deleted with 0 addition(s) and 1 deletion(s)"),
|
||||
).toBeVisible();
|
||||
await expect(page.locator("text=.hidden deleted")).toBeVisible();
|
||||
});
|
||||
|
||||
test("navigation to source tree at specific revision", async ({ page }) => {
|
||||
await page.goto(
|
||||
`${sourceBrowsingFixture}/commits/0801aceeab500033f8d608778218657bd626ef73`,
|
||||
);
|
||||
|
||||
// Go to source tree at this revision.
|
||||
await page.getByTitle("View file").click();
|
||||
await expect(
|
||||
page.locator("text=Add a deeply nested directory tree"),
|
||||
).toBeVisible();
|
||||
await expect(page).toHaveURL(
|
||||
`${sourceBrowsingFixture}/tree/0801aceeab500033f8d608778218657bd626ef73/deep/directory/hierarchy/is/entirely/possible/in/git/repositories/.gitkeep`,
|
||||
);
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"0801aceeab500033f8d608778218657bd626ef73",
|
||||
);
|
||||
await expect(page.locator(".source-tree >> text=.gitkeep")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(
|
||||
"text=deep/directory/hierarchy/is/entirely/possible/in/git/repositories/",
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
const sourceBrowsingFixture =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o";
|
||||
|
||||
test("peer and branch switching", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
// Alice's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=alice hyn1mj").click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("alice delegate");
|
||||
|
||||
await expect(page.getByText("Thursday, November 17, 2022")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".commit-group-headers .commit-teaser"),
|
||||
).toHaveCount(7);
|
||||
|
||||
const latestCommit = page.locator(".commit-teaser").first();
|
||||
await expect(latestCommit).toContainText("Add Markdown cheat sheet");
|
||||
await expect(latestCommit).toContainText("530aabd");
|
||||
|
||||
const earliestCommit = page.locator(".commit-teaser").last();
|
||||
await expect(earliestCommit).toContainText(
|
||||
"Initialize an empty git repository",
|
||||
);
|
||||
await expect(earliestCommit).toContainText("36d5bbe");
|
||||
|
||||
await page.getByTitle("Change branch").click();
|
||||
await page.locator("text=feature/branch").click();
|
||||
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"feature/branch d6318f7",
|
||||
);
|
||||
await expect(page.getByText("Thursday, November 17, 2022")).toBeVisible();
|
||||
await expect(page.locator(".commit-group-headers .commit")).toHaveCount(10);
|
||||
|
||||
await page.getByTitle("Change branch").click();
|
||||
await page.locator("text=orphaned-branch").click();
|
||||
|
||||
await expect(page.getByTitle("Current branch")).toContainText(
|
||||
"orphaned-branch af3641c",
|
||||
);
|
||||
await expect(page.getByText("Thursday, November 17, 2022")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".commit-group-headers .commit-teaser"),
|
||||
).toHaveCount(1);
|
||||
}
|
||||
|
||||
// Bob's peer.
|
||||
{
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=bob hyy1k6").click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("bob");
|
||||
|
||||
await expect(page.getByText("Monday, November 21, 2022")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".commit-group-headers").first().locator(".commit-teaser"),
|
||||
).toHaveCount(1);
|
||||
|
||||
await expect(page.getByText("Thursday, November 17, 2022")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".commit-group-headers").last().locator(".commit-teaser"),
|
||||
).toHaveCount(7);
|
||||
|
||||
await page.pause();
|
||||
const latestCommit = page.locator(".commit-teaser").first();
|
||||
await expect(latestCommit).toContainText("Update readme");
|
||||
await expect(latestCommit).toContainText("0be0f03");
|
||||
|
||||
const earliestCommit = page.locator(".commit-teaser").last();
|
||||
await expect(earliestCommit).toContainText(
|
||||
"Initialize an empty git repository",
|
||||
);
|
||||
await expect(earliestCommit).toContainText("36d5bbe");
|
||||
}
|
||||
});
|
||||
|
||||
test("verified badge", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=bob hyy1k6").click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("bob");
|
||||
|
||||
await page.locator("text=Verified").hover();
|
||||
|
||||
await expect(
|
||||
page.locator(
|
||||
"text=This commit was signed with the committer's radicle key.",
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.locator(
|
||||
"text=bob committed hyy1k6ggg45pi7ip7ksyn1wt1ob4w5zh1awtg4qu3cxmbh5mws8pj1",
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("relative timestamps", async ({ page }) => {
|
||||
page.addInitScript(() => {
|
||||
window.initializeTestStubs = () => {
|
||||
window.e2eTestStubs.FakeTimers.install({
|
||||
now: new Date("November 24 2022 12:00:00").valueOf(),
|
||||
shouldClearNativeTimers: true,
|
||||
shouldAdvanceTime: false,
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=bob hyy1k6").click();
|
||||
await expect(page.getByTitle("Change peer")).toHaveText("bob");
|
||||
|
||||
const latestCommit = page.locator(".commit-teaser").first();
|
||||
await expect(latestCommit).toContainText("bob committed 3 days ago");
|
||||
await expect(latestCommit).toContainText("0be0f03");
|
||||
|
||||
const earliestCommit = page.locator(".commit").last();
|
||||
await expect(earliestCommit).toContainText(
|
||||
"Alice Liddell committed 7 days ago",
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,137 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
import {
|
||||
expectBackAndForwardNavigationWorks,
|
||||
expectUrlPersistsReload,
|
||||
} from "@tests/support/router.js";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.HASH_ROUTING = true;
|
||||
});
|
||||
});
|
||||
|
||||
test("navigate between landing and project page", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.APP_CONFIG = {
|
||||
walletConnect: {
|
||||
bridge: "https://radicle.bridge.walletconnect.org",
|
||||
},
|
||||
reactions: [],
|
||||
seeds: {
|
||||
pinned: [{ host: "0.0.0.0", emoji: "🚀" }],
|
||||
},
|
||||
projects: {
|
||||
pinned: [
|
||||
{
|
||||
name: "source-browsing",
|
||||
urn: "rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
seed: "0.0.0.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await page.goto("/#/");
|
||||
await expect(page).toHaveURL("/#/");
|
||||
|
||||
await page.locator("text=source-browsing").click();
|
||||
await expect(page).toHaveURL(
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks("/#/", page);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigation between seed and project pages", async ({ page }) => {
|
||||
await page.goto("/#/seeds/radicle.local");
|
||||
|
||||
const project = page.locator(".project");
|
||||
await project.click();
|
||||
await expect(page).toHaveURL(
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks("/#/seeds/radicle.local", page);
|
||||
await expectUrlPersistsReload(page);
|
||||
|
||||
await page.locator('role=button[name="Seed"]').click();
|
||||
await expect(page).toHaveURL("/#/seeds/0.0.0.0");
|
||||
});
|
||||
|
||||
test.describe("project page navigation", () => {
|
||||
test("navigation between commit history and single commit", async ({
|
||||
page,
|
||||
}) => {
|
||||
const projectHistoryURL =
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/history/530aabdcc80397af254bc488b767169b92496e81";
|
||||
await page.goto(projectHistoryURL);
|
||||
|
||||
await page.locator("text=Add Markdown cheat sheet").click();
|
||||
await expect(page).toHaveURL(
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/commits/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(projectHistoryURL, page);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigate between tree and commit history", async ({ page }) => {
|
||||
const projectTreeURL =
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81";
|
||||
|
||||
await page.goto(projectTreeURL);
|
||||
await expect(page).toHaveURL(projectTreeURL);
|
||||
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
await expect(page).toHaveURL(
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/history/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(projectTreeURL, page);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigate project paths", async ({ page }) => {
|
||||
const projectTreeURL =
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81";
|
||||
|
||||
await page.goto(projectTreeURL);
|
||||
await expect(page).toHaveURL(projectTreeURL);
|
||||
|
||||
await page.locator("text=.hidden").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/.hidden`);
|
||||
|
||||
await page.locator("text=bin/").click();
|
||||
await page.locator("text=true").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/bin/true`);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(
|
||||
`${projectTreeURL}/.hidden`,
|
||||
page,
|
||||
);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigate project paths with a selected peer", async ({ page }) => {
|
||||
const projectTreeURL =
|
||||
"/#/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/remotes/hyn1mjueopwzrmb18c3zmgg8ei8qunn5wpg76ouymytfqkfxqx7bun/tree";
|
||||
|
||||
await page.goto(projectTreeURL);
|
||||
await expect(page).toHaveURL(projectTreeURL);
|
||||
|
||||
await page.locator("text=.hidden").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/main/.hidden`);
|
||||
|
||||
await page.locator("text=bin/").click();
|
||||
await page.locator("text=true").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/main/bin/true`);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(
|
||||
`${projectTreeURL}/main/.hidden`,
|
||||
page,
|
||||
);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
import {
|
||||
expectBackAndForwardNavigationWorks,
|
||||
expectUrlPersistsReload,
|
||||
} from "@tests/support/router.js";
|
||||
|
||||
test("navigate between landing and project page", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.APP_CONFIG = {
|
||||
walletConnect: {
|
||||
bridge: "https://radicle.bridge.walletconnect.org",
|
||||
},
|
||||
reactions: [],
|
||||
seeds: {
|
||||
pinned: [{ host: "0.0.0.0", emoji: "🚀" }],
|
||||
},
|
||||
projects: {
|
||||
pinned: [
|
||||
{
|
||||
name: "source-browsing",
|
||||
urn: "rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
seed: "0.0.0.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
await page.goto("/");
|
||||
await expect(page).toHaveURL("/");
|
||||
|
||||
await page.locator("text=source-browsing").click();
|
||||
await expect(page).toHaveURL(
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks("/", page);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigation between seed and project pages", async ({ page }) => {
|
||||
await page.goto("/seeds/radicle.local");
|
||||
|
||||
const project = page.locator(".project");
|
||||
await project.click();
|
||||
await expect(page).toHaveURL(
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks("/seeds/radicle.local", page);
|
||||
await expectUrlPersistsReload(page);
|
||||
|
||||
await page.locator('role=button[name="Seed"]').click();
|
||||
await expect(page).toHaveURL("/seeds/0.0.0.0");
|
||||
});
|
||||
|
||||
test.describe("project page navigation", () => {
|
||||
test("navigation between commit history and single commit", async ({
|
||||
page,
|
||||
}) => {
|
||||
const projectHistoryURL =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/history/530aabdcc80397af254bc488b767169b92496e81";
|
||||
await page.goto(projectHistoryURL);
|
||||
|
||||
await page.locator("text=Add Markdown cheat sheet").click();
|
||||
await expect(page).toHaveURL(
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/commits/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(projectHistoryURL, page);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigate between tree and commit history", async ({ page }) => {
|
||||
const projectTreeURL =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81";
|
||||
|
||||
await page.goto(projectTreeURL);
|
||||
await expect(page).toHaveURL(projectTreeURL);
|
||||
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
await expect(page).toHaveURL(
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/history/530aabdcc80397af254bc488b767169b92496e81",
|
||||
);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(projectTreeURL, page);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigate project paths", async ({ page }) => {
|
||||
const projectTreeURL =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/530aabdcc80397af254bc488b767169b92496e81";
|
||||
|
||||
await page.goto(projectTreeURL);
|
||||
await expect(page).toHaveURL(projectTreeURL);
|
||||
|
||||
await page.locator("text=.hidden").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/.hidden`);
|
||||
|
||||
await page.locator("text=bin/").click();
|
||||
await page.locator("text=true").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/bin/true`);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(
|
||||
`${projectTreeURL}/.hidden`,
|
||||
page,
|
||||
);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
|
||||
test("navigate project paths with a selected peer", async ({ page }) => {
|
||||
const projectTreeURL =
|
||||
"seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/remotes/hyn1mjueopwzrmb18c3zmgg8ei8qunn5wpg76ouymytfqkfxqx7bun/tree";
|
||||
|
||||
await page.goto(projectTreeURL);
|
||||
await expect(page).toHaveURL(projectTreeURL);
|
||||
|
||||
await page.locator("text=.hidden").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/main/.hidden`);
|
||||
|
||||
await page.locator("text=bin/").click();
|
||||
await page.locator("text=true").click();
|
||||
await expect(page).toHaveURL(`${projectTreeURL}/main/bin/true`);
|
||||
|
||||
await expectBackAndForwardNavigationWorks(
|
||||
`${projectTreeURL}/main/.hidden`,
|
||||
page,
|
||||
);
|
||||
await expectUrlPersistsReload(page);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
test("navigate to existing project", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
const searchInput = page.getByPlaceholder("Search a name or address…");
|
||||
await searchInput.click();
|
||||
await searchInput.fill("rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o");
|
||||
await searchInput.press("Enter");
|
||||
|
||||
await expect(page).toHaveURL(
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree",
|
||||
);
|
||||
await expect(searchInput).not.toHaveValue(
|
||||
"rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o",
|
||||
);
|
||||
});
|
||||
|
||||
test("navigate to a project that does not exist", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
const searchInput = page.getByPlaceholder("Search a name or address…");
|
||||
await searchInput.click();
|
||||
await searchInput.fill("rad:git:hnrkn1ah5im83fwt4u3jfs5ndwpt9hrnm9wby");
|
||||
await searchInput.press("Enter");
|
||||
|
||||
await page.waitForSelector(".search-bar.shaking");
|
||||
|
||||
await expect(page).toHaveURL("/");
|
||||
await expect(searchInput).toHaveValue(
|
||||
"rad:git:hnrkn1ah5im83fwt4u3jfs5ndwpt9hrnm9wby",
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
test("seed metadata", async ({ page }) => {
|
||||
await page.goto("/seeds/radicle.local");
|
||||
|
||||
await expect(page.locator("header").getByText("radicle.local")).toBeVisible();
|
||||
await expect(
|
||||
page.locator(".title >> text=radicle.local").getByRole("img"),
|
||||
).toHaveAttribute("alt", "🚀");
|
||||
|
||||
await expect(page.getByRole("link", { name: "radicle.local" })).toBeVisible();
|
||||
await expect(page.locator(".seed-address").getByRole("img")).toHaveAttribute(
|
||||
"alt",
|
||||
"🚀",
|
||||
);
|
||||
await expect(page.locator("text=hyb6i8…sg6nn4")).toBeVisible();
|
||||
await expect(page.locator("text=8777")).toBeVisible();
|
||||
await expect(page.locator("text=0.2.0")).toBeVisible();
|
||||
});
|
||||
|
||||
test("seed projects", async ({ page }) => {
|
||||
await page.goto("/seeds/radicle.local");
|
||||
const project = page.locator(".project");
|
||||
|
||||
// Project metadata.
|
||||
{
|
||||
await expect(project.locator("text=source-browsing")).toBeVisible();
|
||||
await expect(
|
||||
project.locator("text=Git repository for source browsing tests"),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
project.locator("text=530aabdcc80397af254bc488b767169b92496e81"),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
// Show project URN on hover.
|
||||
{
|
||||
await expect(
|
||||
project.locator("text=rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o"),
|
||||
).not.toBeVisible();
|
||||
await project.hover();
|
||||
await expect(
|
||||
project.locator("text=rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o"),
|
||||
).toBeVisible();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
const sourceBrowsingFixture =
|
||||
"/seeds/0.0.0.0/rad:git:hnrkgd7sjt79k4j59ddh11ooxg18rk7soej8o/tree/main/src/true.c";
|
||||
|
||||
test("default settings", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
// Default settings.
|
||||
{
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||
await expect(page.locator("html")).toHaveAttribute(
|
||||
"data-codefont",
|
||||
"jetbrains",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("settings persistance", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
page.locator('button[name="Settings"]').click();
|
||||
|
||||
await page.locator(".theme .toggle").click();
|
||||
await page.getByText("Code font").click();
|
||||
await page.getByText("System").click();
|
||||
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
|
||||
await expect(page.locator("html")).toHaveAttribute("data-codefont", "system");
|
||||
|
||||
page.reload();
|
||||
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
|
||||
await expect(page.locator("html")).toHaveAttribute("data-codefont", "system");
|
||||
});
|
||||
|
||||
test("change theme", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
page.locator('button[name="Settings"]').click();
|
||||
|
||||
await page.locator(".theme .toggle").click();
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
|
||||
await expect(page.locator("body")).toHaveCSS(
|
||||
"background-color",
|
||||
"rgb(243, 246, 253)",
|
||||
);
|
||||
// Source highlighting reacts to theme change.
|
||||
await expect(page.getByText("() {")).toHaveCSS("color", "rgb(79, 91, 102)");
|
||||
|
||||
await page.locator(".theme .toggle").click();
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||
await expect(page.locator("body")).toHaveCSS(
|
||||
"background-color",
|
||||
"rgb(11, 19, 26)",
|
||||
);
|
||||
// Source highlighting reacts to theme change.
|
||||
await expect(page.getByText("() {")).toHaveCSS("color", "rgb(192, 197, 206)");
|
||||
});
|
||||
|
||||
test("change code font", async ({ page }) => {
|
||||
await page.goto(sourceBrowsingFixture);
|
||||
|
||||
page.locator('button[name="Settings"]').click();
|
||||
await page.getByText("Code font").click();
|
||||
|
||||
await page.getByText("System").click();
|
||||
await expect(page.getByText("System")).toHaveClass(/active/);
|
||||
await expect(page.locator("html")).toHaveAttribute("data-codefont", "system");
|
||||
|
||||
await page.getByText("JetBrains Mono").click();
|
||||
await expect(page.getByText("JetBrains Mono")).toHaveClass(/active/);
|
||||
await expect(page.locator("html")).toHaveAttribute(
|
||||
"data-codefont",
|
||||
"jetbrains",
|
||||
);
|
||||
});
|
||||
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,151 @@
|
||||
import type * as Stream from "node:stream";
|
||||
|
||||
import * as Fs from "node:fs/promises";
|
||||
import * as Path from "node:path";
|
||||
import { test as base, expect } from "@playwright/test";
|
||||
|
||||
import * as logLabel from "@tests/support/logLabel.js";
|
||||
|
||||
export { expect };
|
||||
|
||||
export const test = base.extend<{
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
forAllTests: void;
|
||||
customAppConfig: boolean;
|
||||
stateDir: string;
|
||||
outputLog: Stream.Writable;
|
||||
}>({
|
||||
customAppConfig: [false, { option: true }],
|
||||
|
||||
forAllTests: [
|
||||
async ({ customAppConfig, outputLog, page }, use) => {
|
||||
const browserLabel = logLabel.make("browser");
|
||||
page.on("console", msg => {
|
||||
// Ignore common console logs that we don't care about.
|
||||
if (
|
||||
msg
|
||||
.text()
|
||||
.startsWith(
|
||||
`Module "buffer" has been externalized for browser compatibility.`,
|
||||
) ||
|
||||
msg.text().startsWith("[vite] connected.") ||
|
||||
msg.text().startsWith("[vite] connecting...") ||
|
||||
msg
|
||||
.text()
|
||||
.includes("Please make sure it wasn't preloaded for nothing.")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
log(msg.text(), browserLabel, outputLog);
|
||||
});
|
||||
|
||||
page.on("pageerror", msg => {
|
||||
expect(
|
||||
false,
|
||||
`Test failed because there was a console error in the app: ${msg}`,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
if (!customAppConfig) {
|
||||
// Remember: `page.addInitScript()` is run in the browser which
|
||||
// is completely isolated from the test environment, so we don't have
|
||||
// access to any variables that we have in the test.
|
||||
await page.addInitScript(() => {
|
||||
window.APP_CONFIG = {
|
||||
walletConnect: {
|
||||
bridge: "https://radicle.bridge.walletconnect.org",
|
||||
},
|
||||
reactions: [],
|
||||
seeds: {
|
||||
pinned: [{ host: "0.0.0.0", emoji: "🚀" }],
|
||||
},
|
||||
projects: { pinned: [] },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const playwrightLabel = logLabel.make("playwright");
|
||||
await page.route("**/*", route => {
|
||||
if (
|
||||
route.request().url().startsWith("http://127.0.0.1") ||
|
||||
route.request().url().startsWith("http://localhost") ||
|
||||
route.request().url().startsWith("http://0.0.0.0")
|
||||
) {
|
||||
return route.continue();
|
||||
} else if (
|
||||
route
|
||||
.request()
|
||||
.url()
|
||||
.startsWith("https://www.gravatar.com/avatar/") ||
|
||||
route.request().url().endsWith(".png")
|
||||
) {
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
path: "./public/favicon.ico",
|
||||
});
|
||||
} else {
|
||||
log(
|
||||
`Aborted remote request: ${route.request().url()}`,
|
||||
playwrightLabel,
|
||||
outputLog,
|
||||
);
|
||||
return route.abort();
|
||||
}
|
||||
});
|
||||
|
||||
page.on("websocket", ws => {
|
||||
log(`WebSocket opened: ${ws.url()}`, playwrightLabel, outputLog);
|
||||
ws.on("framesent", event =>
|
||||
log(
|
||||
`WebSocket framesent: ${event.payload}`,
|
||||
playwrightLabel,
|
||||
outputLog,
|
||||
),
|
||||
);
|
||||
ws.on("framereceived", event =>
|
||||
log(
|
||||
`WebSocket framereceived: ${event.payload}`,
|
||||
playwrightLabel,
|
||||
outputLog,
|
||||
),
|
||||
);
|
||||
ws.on("close", () =>
|
||||
log(`WebSocket closed`, playwrightLabel, outputLog),
|
||||
);
|
||||
});
|
||||
|
||||
await use();
|
||||
},
|
||||
{ scope: "test", auto: true },
|
||||
],
|
||||
|
||||
outputLog: async ({ stateDir }, use) => {
|
||||
const logFile = await Fs.open(Path.join(stateDir, "test.log"), "a");
|
||||
await use(logFile.createWriteStream());
|
||||
await logFile.close();
|
||||
},
|
||||
|
||||
// eslint-disable-next-line no-empty-pattern
|
||||
stateDir: async ({}, use, testInfo) => {
|
||||
const stateDir = testInfo.outputDir;
|
||||
await Fs.rm(stateDir, { recursive: true, force: true });
|
||||
await Fs.mkdir(stateDir, { recursive: true });
|
||||
|
||||
await use(stateDir);
|
||||
if (process.env.CI && testInfo.status === "passed") {
|
||||
await Fs.rm(stateDir, { recursive: true });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function log(text: string, label: string, outputLog: Stream.Writable) {
|
||||
const output = text
|
||||
.split("\n")
|
||||
.map(line => `${label}${line}`)
|
||||
.join("\n");
|
||||
|
||||
outputLog.write(`${output}\n`);
|
||||
if (!process.env.CI) {
|
||||
console.log(output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { FullConfig } from "@playwright/test";
|
||||
|
||||
export default async function globalSetup(_config: FullConfig): Promise<void> {
|
||||
assertHttpApiRunning();
|
||||
}
|
||||
|
||||
// Assert that the test http-api is running. If it is not running, throw an
|
||||
// error that explains how to run it.
|
||||
async function assertHttpApiRunning(): Promise<void> {
|
||||
const palmTestFixtureSeedId =
|
||||
"hyb6i8oggc3mgra9siy8yuohhtz34r98pcybja97c9o789wpsg6nn4";
|
||||
|
||||
const notRunningMessage =
|
||||
"The http-api server with test fixtures needs to be running.\n" +
|
||||
"👉 You can start it with `./scripts/run-http-api-with-fixtures`\n";
|
||||
|
||||
let peerId: string | undefined = undefined;
|
||||
|
||||
try {
|
||||
const response = await fetch("http://0.0.0.0:8777");
|
||||
const data = await response.json();
|
||||
peerId = data.peer.id;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw new Error(notRunningMessage);
|
||||
}
|
||||
|
||||
if (peerId !== palmTestFixtureSeedId) {
|
||||
const wrongSeedMessage =
|
||||
"The server on port 8777 doesn't have the right fixtures.\n";
|
||||
throw new Error(wrongSeedMessage + notRunningMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { ColorName } from "chalk";
|
||||
|
||||
import chalk from "chalk";
|
||||
|
||||
const PADDING_WIDTH = 12;
|
||||
|
||||
// The order here is important, we want successive prefixes to have
|
||||
// high contrast.
|
||||
const availableColors: ColorName[] = [
|
||||
"blue",
|
||||
"yellowBright",
|
||||
"greenBright",
|
||||
"gray",
|
||||
"green",
|
||||
"blueBright",
|
||||
"redBright",
|
||||
"white",
|
||||
"yellow",
|
||||
"red",
|
||||
"magenta",
|
||||
"cyan",
|
||||
];
|
||||
|
||||
const assignedColors: Record<string, ColorName> = {};
|
||||
|
||||
export function make(label: string): string {
|
||||
if (assignedColors[label] === undefined) {
|
||||
const color = availableColors.pop();
|
||||
if (!color) {
|
||||
throw new Error("We're out of colors. 🤷");
|
||||
}
|
||||
|
||||
assignedColors[label] = color;
|
||||
}
|
||||
|
||||
// We reset colors at the beginning of each line to avoid styles from previous
|
||||
// lines messing up prefix colors. This is noticable in rust stack traces
|
||||
// where the `in` and `with` keywords have a white background color.
|
||||
return chalk.reset[assignedColors[label]](
|
||||
`${label.padEnd(PADDING_WIDTH)} | `,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { expect } from "@tests/support/fixtures.js";
|
||||
|
||||
// Reloads the current page and verifies that the URL stays correct
|
||||
export const expectUrlPersistsReload = async (page: Page) => {
|
||||
const url = page.url();
|
||||
await page.reload();
|
||||
await expect(page).toHaveURL(url);
|
||||
};
|
||||
|
||||
// Navigates back, checks the URL and navigates forward back to the initial page
|
||||
export const expectBackAndForwardNavigationWorks = async (
|
||||
beforeURL: string,
|
||||
page: Page,
|
||||
) => {
|
||||
const currentURL = page.url();
|
||||
await page.goBack();
|
||||
await expect(page).toHaveURL(beforeURL);
|
||||
await page.goForward();
|
||||
await expect(page).toHaveURL(currentURL);
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import type { Wallet } from "@app/wallet";
|
||||
|
||||
import { BigNumber } from "ethers";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import * as utils from "./utils";
|
||||
import * as utils from "@app/utils";
|
||||
|
||||
describe("Conversions", () => {
|
||||
test("toWei", () => {
|
||||
+4
-3
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"include": ["src", "cypress/support"],
|
||||
"include": ["src", "tests"],
|
||||
"exclude": ["node_modules/*"],
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"module": "es2020",
|
||||
"types": ["svelte", "vite/client", "cypress"],
|
||||
"types": ["svelte", "vite/client"],
|
||||
"sourceMap": true,
|
||||
"baseUrl": "./",
|
||||
"moduleResolution": "node",
|
||||
@@ -18,7 +18,8 @@
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@public/*": ["./public/*"],
|
||||
"@app/*": ["./src/*"]
|
||||
"@app/*": ["./src/*"],
|
||||
"@tests/*": ["./tests/*"]
|
||||
}
|
||||
},
|
||||
"noEmit": true
|
||||
|
||||
+23
-18
@@ -1,10 +1,27 @@
|
||||
///<reference types="vitest" />
|
||||
import type { UserConfig } from "vite";
|
||||
/// <reference types="vitest" />
|
||||
|
||||
import path from "path";
|
||||
import pluginRewriteAll from "vite-plugin-rewrite-all";
|
||||
import { defineConfig } from "vite";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
const config: UserConfig = {
|
||||
function defineConstants() {
|
||||
const constants = {
|
||||
VITEST: process.env.VITEST !== undefined,
|
||||
PLAYWRIGHT: process.env.PLAYWRIGHT_TEST_BASE_URL !== undefined,
|
||||
};
|
||||
|
||||
// Don't overwrite HASH_ROUTING in Playwright tests, so we can control it
|
||||
// from within the tests.
|
||||
if (process.env.PLAYWRIGHT_TEST_BASE_URL !== undefined) {
|
||||
return constants;
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
return { ...constants, HASH_ROUTING: Boolean(process.env.HASH_ROUTING) };
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
optimizeDeps: {
|
||||
exclude: ["@pedrouid/environment", "@pedrouid/iso-crypto"],
|
||||
},
|
||||
@@ -14,7 +31,7 @@ const config: UserConfig = {
|
||||
},
|
||||
setupFiles: "./vitest/setupVitest",
|
||||
environment: "happy-dom",
|
||||
include: ["**/*.test.ts"],
|
||||
include: ["tests/unit/**/*.test.ts"],
|
||||
reporters: "verbose",
|
||||
},
|
||||
plugins: [
|
||||
@@ -37,13 +54,6 @@ const config: UserConfig = {
|
||||
"@app": path.resolve("./src"),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
"process.env": {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
READABLE_STREAM: "disable",
|
||||
hashRouting: Boolean(process.env.HASH_ROUTING),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: "build",
|
||||
rollupOptions: {
|
||||
@@ -58,11 +68,6 @@ const config: UserConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// For Vitest to work we need to unset READABLE_STREAM.
|
||||
if (process.env.VITEST || process.env.Cypress) {
|
||||
config.define = undefined;
|
||||
}
|
||||
|
||||
export default config;
|
||||
define: defineConstants(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user