mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
Add support for Heartwood
Co-authored-by: Rūdolfs Ošiņš <rudolfs@osins.org> Signed-off-by: xphoniex <dj.2dixx@gmail.com> Signed-off-by: Rūdolfs Ošiņš <rudolfs@osins.org>
This commit is contained in:
co-authored by
Rūdolfs Ošiņš
parent
adc159a71e
commit
f72405a3e4
@@ -12,7 +12,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
browser: [chromium, firefox, visual]
|
||||
browser: [chromium, firefox, visual, heartwood]
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -59,12 +59,19 @@ jobs:
|
||||
run: npx playwright install-deps
|
||||
|
||||
- name: Start http-api test server
|
||||
run: ./scripts/run-http-api-with-fixtures --non-interactive --detach
|
||||
run: |
|
||||
if [ ${{ matrix.browser }} = "heartwood" ]; then
|
||||
./scripts/run-httpd-with-fixtures --non-interactive --detach
|
||||
else
|
||||
./scripts/run-http-api-with-fixtures --non-interactive --detach
|
||||
fi
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: |
|
||||
if [ ${{ matrix.browser }} = "visual" ] && [ ${{ github.ref }} = "refs/heads/master" ]; then
|
||||
npm run test:e2e -- --project ${{ matrix.browser }} --update-snapshots;
|
||||
elif [ ${{ matrix.browser }} = "heartwood" ]; then
|
||||
HEARTWOOD=true npm run test:e2e -- --project chromium;
|
||||
else
|
||||
npm run test:e2e -- --project ${{ matrix.browser }};
|
||||
fi
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
PASSPHRASE=asdf
|
||||
SEED="0.0.0.0:8080"
|
||||
|
||||
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_PASSPHRASE=$PASSPHRASE rad auth
|
||||
|
||||
GIT_AUTHOR_NAME="Alice Liddell"
|
||||
GIT_AUTHOR_EMAIL="alice@radicle.xyz"
|
||||
GIT_COMMITTER_NAME="Alice Liddell"
|
||||
GIT_COMMITTER_EMAIL="alice@radicle.xyz"
|
||||
|
||||
RAD_DEBUG=1 RAD_HOME=$ALICE_RAD_HOME RAD_PASSPHRASE=$PASSPHRASE rad auth
|
||||
|
||||
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_PASSPHRASE=$PASSPHRASE rad init --name "source-browsing" --description "Git repository for source browsing tests" --default-branch "main" --no-confirm
|
||||
PROJECT_ID=$(RAD_HOME=$ALICE_RAD_HOME RAD_PASSPHRASE=$PASSPHRASE rad .)
|
||||
|
||||
cd $BASE_PATH
|
||||
tar -cjf palm.tar.bz2 --exclude "post-receive" --exclude "pre-receive" -C $ALICE_RAD_HOME .
|
||||
@@ -10,8 +10,7 @@ PASSPHRASE=asdf
|
||||
CONTAINER_NAME=radicle-http-api-with-fixtures
|
||||
HTTP_API_BINARY=radicle-http-api
|
||||
|
||||
show_usage()
|
||||
{
|
||||
show_usage() {
|
||||
echo
|
||||
echo "Starts a http-api backend with test fixtures."
|
||||
echo
|
||||
@@ -26,8 +25,7 @@ show_usage()
|
||||
echo
|
||||
}
|
||||
|
||||
prompt_workspace_removal()
|
||||
{
|
||||
prompt_workspace_removal() {
|
||||
echo "This will irrevocably destroy the following directories:"
|
||||
echo
|
||||
echo $WORKSPACE
|
||||
@@ -35,19 +33,18 @@ prompt_workspace_removal()
|
||||
|
||||
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
|
||||
;;
|
||||
[yY][eE][sS])
|
||||
rm -rf $WORKSPACE
|
||||
echo "Done"
|
||||
;;
|
||||
*)
|
||||
echo "Ok, I won't touch your data."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
prepare_workspace()
|
||||
{
|
||||
prepare_workspace() {
|
||||
echo
|
||||
echo "Unpacking fixture $FIXTURE"
|
||||
mkdir -p $WORKSPACE
|
||||
@@ -94,13 +91,13 @@ DETACH=false
|
||||
|
||||
while [ $# -ne 0 ]; do
|
||||
case "$1" in
|
||||
--binary|-b)
|
||||
--binary | -b)
|
||||
BINARY=true
|
||||
;;
|
||||
--detach|-d)
|
||||
--detach | -d)
|
||||
DETACH=true
|
||||
;;
|
||||
--non-interactive|-n)
|
||||
--non-interactive | -n)
|
||||
NON_INTERACTIVE=true
|
||||
;;
|
||||
*)
|
||||
@@ -120,7 +117,6 @@ else
|
||||
prepare_workspace
|
||||
fi
|
||||
|
||||
|
||||
if [ "$BINARY" = true ]; then
|
||||
run_binary
|
||||
else
|
||||
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
REV=1f55d7a32750b3e63c56aad00370411b90420a29
|
||||
|
||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
FIXTURE=$REPO_ROOT/tests/fixtures/seeds/palm-heartwood.tar.bz2
|
||||
WORKSPACE=$REPO_ROOT/tests/tmp/palm
|
||||
PASSPHRASE=asdf
|
||||
CONTAINER_NAME=radicle-httpd-with-fixtures
|
||||
HTTP_API_BINARY=radicle-httpd
|
||||
|
||||
show_usage() {
|
||||
echo
|
||||
echo "Starts a radicle-httpd backend with test fixtures."
|
||||
echo
|
||||
echo "USAGE:"
|
||||
echo " run-httpd-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
|
||||
if [ "$CI" = true ]; then
|
||||
sudo chown -R 0:0 $WORKSPACE
|
||||
fi
|
||||
}
|
||||
|
||||
run_docker() {
|
||||
echo "Starting docker at container $CONTAINER_NAME at $REV"
|
||||
echo " RAD_HOME=$WORKSPACE RAD_PASSPHRASE=$PASSPHRASE radicle-http"
|
||||
echo
|
||||
|
||||
exec docker run \
|
||||
--init \
|
||||
--publish 8080:8080 \
|
||||
--rm \
|
||||
--name $CONTAINER_NAME \
|
||||
--volume $WORKSPACE:/app/radicle \
|
||||
"$@" \
|
||||
--env "RAD_HOME=/app/radicle" \
|
||||
--env "RAD_PASSPHRASE=$PASSPHRASE" \
|
||||
"gcr.io/radicle-services/radicle-httpd:$REV"
|
||||
}
|
||||
|
||||
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/heartwood"
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Starting $HTTP_API_BINARY"
|
||||
echo " RAD_HOME=$WORKSPACE $HTTP_API_BINARY"
|
||||
echo
|
||||
|
||||
RAD_HOME=$WORKSPACE RAD_PASSPHRASE=$PASSPHRASE $HTTP_API_BINARY
|
||||
}
|
||||
|
||||
BINARY=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
|
||||
@@ -6,7 +6,11 @@
|
||||
|
||||
import Address from "@app/components/Address.svelte";
|
||||
import { Profile, ProfileType } from "@app/lib/profile";
|
||||
import { formatRadicleId, formatTimestamp } from "@app/lib/utils";
|
||||
import {
|
||||
formatRadicleId,
|
||||
formatSeedId,
|
||||
formatTimestamp,
|
||||
} from "@app/lib/utils";
|
||||
|
||||
export let noAvatar = false;
|
||||
export let author: Author;
|
||||
@@ -67,7 +71,7 @@
|
||||
</span>
|
||||
{:else}
|
||||
<span class="highlight">
|
||||
{formatRadicleId(author.id)}
|
||||
{window.HEARTWOOD ? formatSeedId(author.id) : formatRadicleId(author.id)}
|
||||
</span>
|
||||
{/if}
|
||||
<span class="caption"> {caption} </span>
|
||||
|
||||
@@ -20,17 +20,19 @@
|
||||
|
||||
const validationExamples: Record<string, string> = {
|
||||
URL: "https://acme.xyz/",
|
||||
URN: "eip155:1:0xd1bb21bd5a432d2919c82bcefe1bc7f8cc9207d9",
|
||||
ID: "eip155:1:0xd1bb21bd5a432d2919c82bcefe1bc7f8cc9207d9",
|
||||
handle: "acme",
|
||||
id: "hydkkcf6k9be5fuszdhpqbctu3q3fuwagj874wx2puia8ti8coygh1",
|
||||
identity: "rad:git:hnrkqdpm9ub19oc8dccx44echy75hzfsezyio",
|
||||
identity: window.HEARTWOOD
|
||||
? "rad:zKtT7DmF9H34KkvcKj9PHW19WzjT"
|
||||
: "rad:git:hnrkqdpm9ub19oc8dccx44echy75hzfsezyio",
|
||||
domain: "seed.acme.xyz",
|
||||
address: "0x17a8c096733BD5F87aD43D7A2A4d1C42ab8A2A70",
|
||||
};
|
||||
|
||||
const validationTypes: { [index: string]: RegExp } = {
|
||||
URL: /^(https:\/\/|http:\/\/|ipfs:\/\/)\S+/,
|
||||
URN: /^[a-z]+:[a-zA-Z0-9:-]{1,64}$/,
|
||||
ID: /^z[a-z]+:[a-zA-Z0-9:-]{1,64}$/,
|
||||
// Github
|
||||
// Username may only contain alphanumeric characters or hyphens.
|
||||
// Username cannot have multiple consecutive hyphens.
|
||||
@@ -43,7 +45,9 @@
|
||||
handle: /^[a-zA-Z0-9-_]{1,39}$/,
|
||||
address: /^0x[a-zA-Z0-9]{40}$/,
|
||||
id: /^[a-z0-9]+$/,
|
||||
identity: /^rad:git:[a-z0-9]{37}$/,
|
||||
identity: window.HEARTWOOD
|
||||
? /^rad:[a-z0-9]{28}$/
|
||||
: /^rad:git:[a-z0-9]{37}$/,
|
||||
domain: /^[^/:$!_;,@#]+\.[a-z]{2,}$/,
|
||||
};
|
||||
</script>
|
||||
|
||||
Vendored
+2
@@ -10,6 +10,8 @@ declare global {
|
||||
PLAYWRIGHT: boolean;
|
||||
HASH_ROUTING: boolean;
|
||||
|
||||
HEARTWOOD: 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
|
||||
|
||||
+5
-1
@@ -13,7 +13,11 @@ export class Request {
|
||||
|
||||
constructor(path: string, api: Host) {
|
||||
this.port = api.port || defaultSeedPort;
|
||||
this.base = api.host;
|
||||
if (window.HEARTWOOD) {
|
||||
this.base = api.host + "/api";
|
||||
} else {
|
||||
this.base = api.host;
|
||||
}
|
||||
this.path = path.startsWith("/") ? path.slice(1) : path;
|
||||
this.protocol = api.host === "0.0.0.0" ? "http://" : "https://";
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ export interface Diff {
|
||||
moved: string[];
|
||||
copied: string[];
|
||||
modified: FileDiff[];
|
||||
stats: DiffStats;
|
||||
}
|
||||
|
||||
export interface DiffStats {
|
||||
|
||||
+20
-9
@@ -12,7 +12,7 @@ export interface IIssue {
|
||||
author: Author;
|
||||
title: string;
|
||||
state: State;
|
||||
comment: Comment;
|
||||
comment: Comment; // TODO: Remove this after we have migrated to Heartwood.
|
||||
discussion: Thread[];
|
||||
tags: Tag[];
|
||||
timestamp: number;
|
||||
@@ -32,7 +32,8 @@ export interface Comment<R = null> {
|
||||
body: string;
|
||||
reactions: Record<string, number>;
|
||||
timestamp: number;
|
||||
replies: R;
|
||||
replies: R; // TODO: Remove this after we have migrated to Heartwood.
|
||||
replyTo: R;
|
||||
}
|
||||
|
||||
export type Thread = Comment<Comment[]>;
|
||||
@@ -57,7 +58,7 @@ export class Issue {
|
||||
author: Author;
|
||||
title: string;
|
||||
state: State;
|
||||
comment: Comment;
|
||||
comment: Comment; // TODO: Remove this after we have migrated to Heartwood.
|
||||
discussion: Thread[];
|
||||
tags: Tag[];
|
||||
timestamp: number;
|
||||
@@ -67,18 +68,28 @@ export class Issue {
|
||||
this.author = issue.author;
|
||||
this.title = issue.title;
|
||||
this.state = issue.state;
|
||||
this.comment = issue.comment;
|
||||
this.comment = issue.comment; // TODO: Remove this after we have migrated to Heartwood.
|
||||
this.discussion = issue.discussion;
|
||||
this.tags = issue.tags;
|
||||
this.timestamp = issue.timestamp;
|
||||
if (window.HEARTWOOD) {
|
||||
this.timestamp = issue.discussion[0].timestamp;
|
||||
} else {
|
||||
this.timestamp = issue.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// Counts the amount of comments and replies in a discussion
|
||||
countComments(): number {
|
||||
return this.discussion.reduce((acc, comment) => {
|
||||
if (comment.replies) return acc + comment.replies.length + 1; // We add all replies and 1 forathe comment in this loop.
|
||||
return acc + 1; // If there are no replies, we simply add 1 for the comment in this loop.
|
||||
}, 0);
|
||||
if (window.HEARTWOOD) {
|
||||
return this.discussion.reduce(acc => {
|
||||
return acc + 1; // If there are no replies, we simply add 1 for the comment in this loop.
|
||||
}, 0);
|
||||
} else {
|
||||
return this.discussion.reduce((acc, comment) => {
|
||||
if (comment.replies) return acc + comment.replies.length + 1; // We add all replies and 1 forathe comment in this loop.
|
||||
return acc + 1; // If there are no replies, we simply add 1 for the comment in this loop.
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static async getIssues(id: string, host: Host): Promise<Issue[]> {
|
||||
|
||||
+10
-4
@@ -100,10 +100,16 @@ export class Patch implements IPatch {
|
||||
|
||||
// Counts the amount of comments and replies in a discussion
|
||||
countComments(rev: number): number {
|
||||
return this.revisions[rev].discussion.reduce((acc, comment) => {
|
||||
if (comment.replies) return acc + comment.replies.length + 1; // We add all replies and 1 for each comment in this loop.
|
||||
return acc + 1; // If there are no replies, we simply add 1 for the comment in this loop.
|
||||
}, 0);
|
||||
if (window.HEARTWOOD) {
|
||||
return this.revisions[rev].discussion.reduce(acc => {
|
||||
return acc + 1; // If there are no replies, we simply add 1 for the comment in this loop.
|
||||
}, 0);
|
||||
} else {
|
||||
return this.revisions[rev].discussion.reduce((acc, comment) => {
|
||||
if (comment.replies) return acc + comment.replies.length + 1; // We add all replies and 1 for each comment in this loop.
|
||||
return acc + 1; // If there are no replies, we simply add 1 for the comment in this loop.
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
createTimeline(rev: number) {
|
||||
|
||||
+73
-49
@@ -12,6 +12,7 @@ export type Branches = { [key: string]: string };
|
||||
export type MaybeBlob = Blob | undefined;
|
||||
export type MaybeTree = Tree | undefined;
|
||||
|
||||
// TODO: Remove this after we have migrated to Heartwood.
|
||||
export type Delegate =
|
||||
| {
|
||||
type: "indirect";
|
||||
@@ -40,38 +41,43 @@ export interface ProjectInfo {
|
||||
name: string;
|
||||
description: string;
|
||||
defaultBranch: string;
|
||||
delegates: Delegate[];
|
||||
remotes: PeerId[];
|
||||
delegates: Delegate[]; // TODO: Remove this after we have migrated to Heartwood.
|
||||
remotes: PeerId[]; // TODO: Remove this after we have migrated to Heartwood.
|
||||
patches?: number;
|
||||
issues?: number;
|
||||
}
|
||||
|
||||
export interface Tree {
|
||||
path: string;
|
||||
info: EntryInfo;
|
||||
info: EntryInfo; // TODO: Remove this after we have migrated to Heartwood.
|
||||
entries: Array<Entry>;
|
||||
stats: Stats;
|
||||
name: string;
|
||||
kind: Kind;
|
||||
lastCommit: CommitHeader;
|
||||
}
|
||||
|
||||
// TODO: Remove "TREE" and "BLOB" after we have migrated to Heartwood.
|
||||
type Kind = "tree" | "blob" | "TREE" | "BLOB";
|
||||
|
||||
export interface Stats {
|
||||
commits: number;
|
||||
contributors: number;
|
||||
}
|
||||
|
||||
export enum ObjectType {
|
||||
Blob = "BLOB",
|
||||
Tree = "TREE",
|
||||
}
|
||||
|
||||
// TODO: Remove this after we have migrated to Heartwood.
|
||||
export interface EntryInfo {
|
||||
name: string;
|
||||
objectType: ObjectType;
|
||||
objectType: Kind;
|
||||
lastCommit: CommitHeader;
|
||||
}
|
||||
|
||||
export interface Entry {
|
||||
path: string;
|
||||
info: EntryInfo;
|
||||
info: EntryInfo; // TODO: Remove this after we have migrated to Heartwood.
|
||||
name: string;
|
||||
kind: Kind;
|
||||
lastCommit: CommitHeader;
|
||||
}
|
||||
|
||||
export interface Blob {
|
||||
@@ -79,7 +85,10 @@ export interface Blob {
|
||||
html?: boolean;
|
||||
content: string;
|
||||
path: string;
|
||||
info: EntryInfo;
|
||||
info: EntryInfo; // TODO: Remove this after we have migrated to Heartwood.
|
||||
name: string;
|
||||
kind: Kind;
|
||||
lastCommit: CommitHeader;
|
||||
}
|
||||
|
||||
export interface Remote {
|
||||
@@ -144,8 +153,8 @@ export class Project implements ProjectInfo {
|
||||
name: string;
|
||||
description: string;
|
||||
defaultBranch: string;
|
||||
delegates: Delegate[];
|
||||
remotes: PeerId[];
|
||||
delegates: Delegate[]; // TODO: Remove this after we have migrated to Heartwood.
|
||||
remotes: PeerId[]; // TODO: Remove this after we have migrated to Heartwood.
|
||||
seed: Seed;
|
||||
peers: Peer[];
|
||||
branches: Branches;
|
||||
@@ -167,8 +176,8 @@ export class Project implements ProjectInfo {
|
||||
this.name = info.name;
|
||||
this.description = info.description;
|
||||
this.defaultBranch = info.defaultBranch;
|
||||
this.delegates = info.delegates;
|
||||
this.remotes = info.remotes;
|
||||
this.delegates = info.delegates; // TODO: Remove this after we have migrated to Heartwood.
|
||||
this.remotes = info.remotes; // TODO: Remove this after we have migrated to Heartwood.
|
||||
this.seed = seed;
|
||||
this.peers = peers;
|
||||
this.branches = branches;
|
||||
@@ -192,10 +201,14 @@ export class Project implements ProjectInfo {
|
||||
}
|
||||
|
||||
static async getInfo(nameOrId: string, host: Host): Promise<ProjectInfo> {
|
||||
const result = await new Request(`projects/${nameOrId}`, host).get();
|
||||
result["id"] = result["urn"];
|
||||
delete result["urn"];
|
||||
return result;
|
||||
if (window.HEARTWOOD) {
|
||||
return await new Request(`projects/${nameOrId}`, host).get();
|
||||
} else {
|
||||
const result = await new Request(`projects/${nameOrId}`, host).get();
|
||||
result["id"] = result["urn"];
|
||||
delete result["urn"];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static async getProjects(
|
||||
@@ -209,13 +222,17 @@ export class Project implements ProjectInfo {
|
||||
"per-page": opts?.perPage,
|
||||
page: opts?.page,
|
||||
};
|
||||
const results = await new Request("projects", host).get(params);
|
||||
results.forEach((result: { [x: string]: any }) => {
|
||||
result["id"] = result["urn"];
|
||||
delete result["urn"];
|
||||
});
|
||||
if (window.HEARTWOOD) {
|
||||
return await new Request("projects", host).get(params);
|
||||
} else {
|
||||
const results = await new Request("projects", host).get(params);
|
||||
results.forEach((result: { [x: string]: any }) => {
|
||||
result["id"] = result["urn"];
|
||||
delete result["urn"];
|
||||
});
|
||||
|
||||
return results;
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
static async getDelegateProjects(
|
||||
@@ -280,28 +297,31 @@ export class Project implements ProjectInfo {
|
||||
`projects/${this.id}/commits/${commit}`,
|
||||
this.seed.addr,
|
||||
).get();
|
||||
result.stats["insertions"] = result.stats["additions"];
|
||||
delete result.stats["additions"];
|
||||
result.diff["added"] = result.diff["created"];
|
||||
delete result.diff["created"];
|
||||
|
||||
for (const kind of ["added", "deleted", "modified"]) {
|
||||
for (const file of result.diff[kind]) {
|
||||
for (const hunk of file.diff.hunks) {
|
||||
for (const line of hunk.lines) {
|
||||
if (line["lineNumOld"]) {
|
||||
line["lineNoOld"] = line["lineNumOld"];
|
||||
delete line["lineNumOld"];
|
||||
}
|
||||
if (!window.HEARTWOOD) {
|
||||
result.stats["insertions"] = result.stats["additions"];
|
||||
delete result.stats["additions"];
|
||||
result.diff["added"] = result.diff["created"];
|
||||
delete result.diff["created"];
|
||||
|
||||
if (line["lineNumNew"]) {
|
||||
line["lineNoNew"] = line["lineNumNew"];
|
||||
delete line["lineNumNew"];
|
||||
}
|
||||
for (const kind of ["added", "deleted", "modified"]) {
|
||||
for (const file of result.diff[kind]) {
|
||||
for (const hunk of file.diff.hunks) {
|
||||
for (const line of hunk.lines) {
|
||||
if (line["lineNumOld"]) {
|
||||
line["lineNoOld"] = line["lineNumOld"];
|
||||
delete line["lineNumOld"];
|
||||
}
|
||||
|
||||
if (line["lineNum"]) {
|
||||
line["lineNo"] = line["lineNum"];
|
||||
delete line["lineNum"];
|
||||
if (line["lineNumNew"]) {
|
||||
line["lineNoNew"] = line["lineNumNew"];
|
||||
delete line["lineNumNew"];
|
||||
}
|
||||
|
||||
if (line["lineNum"]) {
|
||||
line["lineNo"] = line["lineNum"];
|
||||
delete line["lineNum"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,12 +384,16 @@ export class Project implements ProjectInfo {
|
||||
const info = await Project.getInfo(id, seed.addr);
|
||||
id = isRadicleId(id) ? id : info.id;
|
||||
|
||||
// Older versions of http-api don't include the ID.
|
||||
if (!info.id) info.id = id;
|
||||
let peers: Peer[] = [];
|
||||
|
||||
const peers: Peer[] = info.delegates
|
||||
? await Project.getRemotes(id, seed.addr)
|
||||
: [];
|
||||
if (window.HEARTWOOD) {
|
||||
peers = await Project.getRemotes(id, seed.addr);
|
||||
} else {
|
||||
// Older versions of http-api don't include the ID.
|
||||
if (!info.id) info.id = id;
|
||||
|
||||
peers = info.delegates ? await Project.getRemotes(id, seed.addr) : [];
|
||||
}
|
||||
|
||||
let remote: Remote = {
|
||||
heads: info.head ? { [info.defaultBranch]: info.head } : {},
|
||||
|
||||
+4
-16
@@ -90,18 +90,8 @@ export async function getRegistration(
|
||||
getText(resolver, "com.github"),
|
||||
]);
|
||||
|
||||
const [
|
||||
address,
|
||||
avatar,
|
||||
url,
|
||||
id,
|
||||
seedId,
|
||||
seedHost,
|
||||
seedGit,
|
||||
seedApi,
|
||||
twitter,
|
||||
github,
|
||||
] = meta.filter(isFulfilled).map(r => (r.value ? r.value : undefined));
|
||||
const [address, avatar, url, id, seedId, seedHost, seedApi, twitter, github] =
|
||||
meta.filter(isFulfilled).map(r => (r.value ? r.value : undefined));
|
||||
|
||||
const profile: EnsProfile = {
|
||||
name,
|
||||
@@ -119,7 +109,6 @@ export async function getRegistration(
|
||||
profile.seed = new Seed({
|
||||
host: seedHost,
|
||||
id: seedId,
|
||||
git: seedGit,
|
||||
addr: seedApi,
|
||||
});
|
||||
} catch (e: any) {
|
||||
@@ -157,10 +146,9 @@ export async function getSeed(
|
||||
return null;
|
||||
}
|
||||
|
||||
const [id, host, git, api] = await Promise.all([
|
||||
const [id, host, api] = await Promise.all([
|
||||
getText(resolver, "eth.radicle.seed.id"),
|
||||
getText(resolver, "eth.radicle.seed.host"),
|
||||
getText(resolver, "eth.radicle.seed.git"),
|
||||
getText(resolver, "eth.radicle.seed.api"),
|
||||
]);
|
||||
|
||||
@@ -170,7 +158,7 @@ export async function getSeed(
|
||||
}
|
||||
|
||||
try {
|
||||
return new Seed({ host, id, git, addr: api });
|
||||
return new Seed({ host, id, addr: api });
|
||||
} catch (e: any) {
|
||||
console.debug(e, host, id);
|
||||
return new InvalidSeed(id, host);
|
||||
|
||||
+36
-21
@@ -23,15 +23,16 @@ export class InvalidSeed {
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultSeedPort = 8777;
|
||||
export const defaultSeedPort = window.HEARTWOOD ? 8080 : 8777;
|
||||
export const defaultNodePort = 8776;
|
||||
// TODO: Remove this after we have migrated to Heartwood.
|
||||
export const defaultGitPort = 443;
|
||||
|
||||
export class Seed {
|
||||
valid = true as const;
|
||||
|
||||
addr: { host: string; port: number | null };
|
||||
git: { host: string; port: number | null };
|
||||
git: { host: string; port: number | null }; // TODO: Remove this after we have migrated to Heartwood.
|
||||
node: { host: string; id: string; port: number };
|
||||
|
||||
version?: string;
|
||||
@@ -40,17 +41,21 @@ export class Seed {
|
||||
constructor(seed: {
|
||||
host: string;
|
||||
id: string;
|
||||
git?: string | null;
|
||||
git?: string | null; // TODO: Remove this after we have migrated to Heartwood.
|
||||
addr?: string | null;
|
||||
version?: string | null;
|
||||
}) {
|
||||
assert(isDomain(seed.host), `invalid seed host: ${seed.host}`);
|
||||
assert(/^[a-z0-9]+$/.test(seed.id), `invalid seed id ${seed.id}`);
|
||||
if (window.HEARTWOOD) {
|
||||
assert(/^[a-zA-Z0-9]+$/.test(seed.id), `invalid seed id ${seed.id}`);
|
||||
} else {
|
||||
assert(/^[a-z0-9]+$/.test(seed.id), `invalid seed id ${seed.id}`);
|
||||
}
|
||||
|
||||
let _seed = null;
|
||||
let _git = null;
|
||||
let _git = null; // TODO: Remove this after we have migrated to Heartwood.
|
||||
let _seedPort: number | null = defaultSeedPort;
|
||||
let _gitPort: number | null = defaultGitPort;
|
||||
let _gitPort: number | null = defaultGitPort; // TODO: Remove this after we have migrated to Heartwood.
|
||||
|
||||
if (seed.addr) {
|
||||
try {
|
||||
@@ -73,15 +78,17 @@ export class Seed {
|
||||
assert(isDomain(_seed), `invalid seed host ${_seed}`);
|
||||
}
|
||||
|
||||
if (seed.git) {
|
||||
try {
|
||||
const url = new URL(seed.git);
|
||||
_git = url.hostname;
|
||||
_gitPort = url.port ? Number(url.port) : null;
|
||||
} catch {
|
||||
_git = seed.git;
|
||||
if (window.HEARTWOOD) {
|
||||
if (seed.git) {
|
||||
try {
|
||||
const url = new URL(seed.git);
|
||||
_git = url.hostname;
|
||||
_gitPort = url.port ? Number(url.port) : null;
|
||||
} catch {
|
||||
_git = seed.git;
|
||||
}
|
||||
assert(isDomain(_git), `invalid seed git host ${_git}`);
|
||||
}
|
||||
assert(isDomain(_git), `invalid seed git host ${_git}`);
|
||||
}
|
||||
|
||||
this.emoji = getSeedEmoji(seed.host);
|
||||
@@ -89,10 +96,10 @@ export class Seed {
|
||||
// The `_seed` being more specific takes
|
||||
// precedence over the `host`, if available.
|
||||
_seed = _seed ?? seed.host;
|
||||
_git = _git ?? seed.host;
|
||||
_git = _git ?? seed.host; // TODO: Remove this after we have migrated to Heartwood.
|
||||
|
||||
this.addr = { host: _seed, port: _seedPort };
|
||||
this.git = { host: _git, port: _gitPort };
|
||||
this.git = { host: _git, port: _gitPort }; // TODO: Remove this after we have migrated to Heartwood.
|
||||
this.node = { host: seed.host, id: seed.id, port: defaultNodePort };
|
||||
|
||||
if (seed.version) {
|
||||
@@ -121,10 +128,14 @@ export class Seed {
|
||||
? await proj.Project.getDelegateProjects(id, this.addr, { perPage })
|
||||
: await proj.Project.getProjects(this.addr, { perPage });
|
||||
|
||||
return result.map((project: proj.ProjectInfo) => ({
|
||||
...project,
|
||||
id: project.id,
|
||||
}));
|
||||
if (window.HEARTWOOD) {
|
||||
return result;
|
||||
} else {
|
||||
return result.map((project: proj.ProjectInfo) => ({
|
||||
...project,
|
||||
id: project.id,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async getStats(): Promise<{
|
||||
@@ -135,7 +146,11 @@ export class Seed {
|
||||
}
|
||||
|
||||
static async getNode(host: Host): Promise<{ id: string }> {
|
||||
return new Request("/peer", host).get();
|
||||
if (window.HEARTWOOD) {
|
||||
return new Request("/node", host).get();
|
||||
} else {
|
||||
return new Request("/peer", host).get();
|
||||
}
|
||||
}
|
||||
|
||||
static async getInfo(host: Host): Promise<{ version: string }> {
|
||||
|
||||
+15
-3
@@ -105,7 +105,11 @@ export function formatSeedId(id: string): string {
|
||||
export function formatRadicleId(id: string): string {
|
||||
assert(isRadicleId(id));
|
||||
|
||||
return id.substring(0, 14) + "…" + id.substring(id.length - 6, id.length);
|
||||
if (window.HEARTWOOD) {
|
||||
return id.substring(0, 10) + "…" + id.substring(id.length - 6, id.length);
|
||||
} else {
|
||||
return id.substring(0, 14) + "…" + id.substring(id.length - 6, id.length);
|
||||
}
|
||||
}
|
||||
|
||||
export function formatBalance(n: BigNumber, decimals?: number): string {
|
||||
@@ -245,7 +249,11 @@ export const formatTimestamp = (
|
||||
|
||||
// Check whether the input is a Radicle ID.
|
||||
export function isRadicleId(input: string): boolean {
|
||||
return /^rad:[a-z]+:[a-zA-Z0-9]+$/.test(input);
|
||||
if (window.HEARTWOOD) {
|
||||
return /^rad:[a-zA-Z0-9]+$/.test(input);
|
||||
} else {
|
||||
return /^rad:[a-z]+:[a-zA-Z0-9]+$/.test(input);
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the input is a Radicle Peer ID.
|
||||
@@ -295,7 +303,11 @@ export function formatName(input: string, wallet: Wallet): string {
|
||||
|
||||
// Parse a Radicle Id.
|
||||
export function parseRadicleId(id: string): string {
|
||||
return id.replace(/^rad:[a-z]+:/, "");
|
||||
if (window.HEARTWOOD) {
|
||||
return id.replace(/^rad:/, "");
|
||||
} else {
|
||||
return id.replace(/^rad:[a-z]+:/, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Get amount of days passed between two dates without including the end date
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
export let line: string | undefined = undefined;
|
||||
|
||||
const fileExtension = blob.path.split(".").pop() ?? "";
|
||||
const lastCommit = blob.info.lastCommit;
|
||||
const lastCommit = window.HEARTWOOD ? blob.lastCommit : blob.info.lastCommit;
|
||||
const parentDir = blob.path
|
||||
.match(/^.*\/|/)
|
||||
?.values()
|
||||
@@ -213,7 +213,7 @@
|
||||
<span class="file-name">
|
||||
<span class="txt-faded">{parentDir}</span>
|
||||
​
|
||||
<span>{blob.info.name}</span>
|
||||
<span>{window.HEARTWOOD ? blob.name : blob.info.name}</span>
|
||||
</span>
|
||||
<div class="right">
|
||||
{#if isMarkdown}
|
||||
|
||||
@@ -73,5 +73,8 @@
|
||||
{/if}
|
||||
</div>
|
||||
</header>
|
||||
<Changeset stats={commit.stats} diff={commit.diff} on:browse={onBrowse} />
|
||||
<Changeset
|
||||
stats={window.HEARTWOOD ? commit.diff.stats : commit.stats}
|
||||
diff={commit.diff}
|
||||
on:browse={onBrowse} />
|
||||
</div>
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<CommitAuthorship {commit} noDelegate />
|
||||
</div>
|
||||
<div class="column-right">
|
||||
{#if commit.context.committer}
|
||||
{#if !window.HEARTWOOD && commit.context.committer}
|
||||
<div class="layout-desktop">
|
||||
<CommitVerifiedBadge {commit} />
|
||||
</div>
|
||||
|
||||
@@ -98,9 +98,12 @@
|
||||
{revision}
|
||||
on:branchChanged={event => updateRevision(event.detail)} />
|
||||
|
||||
{#if seed.git.host}
|
||||
{#if window.HEARTWOOD && seed.addr.host}
|
||||
<CloneButton seedHost={seed.addr.host} {id} />
|
||||
{:else if seed.git.host}
|
||||
<CloneButton seedHost={seed.git.host} {id} />
|
||||
{/if}
|
||||
|
||||
<span>
|
||||
{#if seed.addr.host}
|
||||
<HeaderToggleLabel
|
||||
|
||||
@@ -136,15 +136,20 @@
|
||||
</header>
|
||||
<main>
|
||||
<div class="comments">
|
||||
<Comment comment={issue.comment} {getImage} {wallet} />
|
||||
{#if !window.HEARTWOOD}
|
||||
<Comment comment={issue.comment} {getImage} {wallet} />
|
||||
{/if}
|
||||
|
||||
{#each issue.discussion as comment}
|
||||
<Comment {comment} {getImage} {wallet} />
|
||||
{#if comment.replies}
|
||||
<div class="replies">
|
||||
{#each comment.replies as reply}
|
||||
<Comment comment={reply} {getImage} {wallet} />
|
||||
{/each}
|
||||
</div>
|
||||
{#if !window.HEARTWOOD}
|
||||
{#if comment.replies}
|
||||
<div class="replies">
|
||||
{#each comment.replies as reply}
|
||||
<Comment comment={reply} {getImage} {wallet} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -138,8 +138,10 @@
|
||||
</div>
|
||||
{:else if activeTab === PatchTab.Diff && revision.changeset}
|
||||
<Changeset
|
||||
stats={window.HEARTWOOD
|
||||
? revision.changeset.diff.stats
|
||||
: revision.changeset.stats}
|
||||
diff={revision.changeset.diff}
|
||||
stats={revision.changeset.stats}
|
||||
on:browse={e => onBrowse(e, revision.oid)} />
|
||||
{:else if activeTab === PatchTab.Diff}
|
||||
<Placeholder emoji="🍳">
|
||||
|
||||
@@ -66,11 +66,13 @@
|
||||
{:else if element.type === TimelineType.Thread}
|
||||
<div class="margin-left">
|
||||
<Comment comment={element.inner} {wallet} {getImage} />
|
||||
<div class="replies">
|
||||
{#each element.inner.replies as comment}
|
||||
<Comment caption="replied" {comment} {wallet} {getImage} />
|
||||
{/each}
|
||||
</div>
|
||||
{#if !window.HEARTWOOD}
|
||||
<div class="replies">
|
||||
{#each element.inner.replies as comment}
|
||||
<Comment caption="replied" {comment} {wallet} {getImage} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import { ObjectType } from "@app/lib/project";
|
||||
|
||||
import File from "./Tree/File.svelte";
|
||||
import Folder from "./Tree/Folder.svelte";
|
||||
|
||||
@@ -20,11 +18,11 @@
|
||||
</script>
|
||||
|
||||
{#each tree.entries as entry (entry.path)}
|
||||
{#if entry.info.objectType === ObjectType.Tree}
|
||||
{#if window.HEARTWOOD ? entry.kind === "tree" : entry.info.objectType === "TREE"}
|
||||
<Folder
|
||||
{fetchTree}
|
||||
{loadingPath}
|
||||
name={entry.info.name}
|
||||
name={window.HEARTWOOD ? entry.name : entry.info.name}
|
||||
prefix={`${entry.path}/`}
|
||||
currentPath={path}
|
||||
on:select={onSelect} />
|
||||
@@ -32,7 +30,7 @@
|
||||
<File
|
||||
active={entry.path === path}
|
||||
loading={entry.path === loadingPath}
|
||||
name={entry.info.name}
|
||||
name={window.HEARTWOOD ? entry.name : entry.info.name}
|
||||
on:click={() => onSelect({ detail: entry.path })} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import type { MaybeTree } from "@app/lib/project";
|
||||
|
||||
import Loading from "@app/components/Loading.svelte";
|
||||
import { ObjectType } from "@app/lib/project";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
import File from "./File.svelte";
|
||||
@@ -75,10 +74,10 @@
|
||||
{:then tree}
|
||||
{#if tree}
|
||||
{#each tree.entries as entry (entry.path)}
|
||||
{#if entry.info.objectType === ObjectType.Tree}
|
||||
{#if window.HEARTWOOD ? entry.kind === "tree" : entry.info.objectType === "TREE"}
|
||||
<svelte:self
|
||||
{fetchTree}
|
||||
name={entry.info.name}
|
||||
name={window.HEARTWOOD ? entry.name : entry.info.name}
|
||||
on:select={onSelectFile}
|
||||
prefix={`${entry.path}/`}
|
||||
{loadingPath}
|
||||
@@ -87,7 +86,7 @@
|
||||
<File
|
||||
active={entry.path === currentPath}
|
||||
loading={entry.path === loadingPath}
|
||||
name={entry.info.name}
|
||||
name={window.HEARTWOOD ? entry.name : entry.info.name}
|
||||
on:click={() => {
|
||||
onSelectFile({ detail: entry.path });
|
||||
}} />
|
||||
|
||||
@@ -124,7 +124,9 @@
|
||||
name: "id",
|
||||
label: "Radicle",
|
||||
validate: "identity",
|
||||
placeholder: "Radicle ID, eg. rad:git:hnrkqdpm9ub19oc8d…",
|
||||
placeholder: window.HEARTWOOD
|
||||
? "Radicle ID, eg. rad:zKtT7DmF9H34KkvcKj…"
|
||||
: "Radicle ID, eg. rad:git:hnrkqdpm9ub19oc8d…",
|
||||
description: "The local radicle identity associated with this name.",
|
||||
value: r.profile.id ?? "",
|
||||
editable: true,
|
||||
|
||||
@@ -251,6 +251,10 @@ test("clone modal", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("peer and branch switching", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(projectFixtureUrl);
|
||||
|
||||
// Alice's peer.
|
||||
@@ -335,6 +339,10 @@ test("peer and branch switching", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("only one modal can be open at a time", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(projectFixtureUrl);
|
||||
|
||||
await page.getByTitle("Change peer").click();
|
||||
@@ -366,6 +374,10 @@ test("only one modal can be open at a time", async ({ page }) => {
|
||||
});
|
||||
|
||||
test.describe("browser error handling", () => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
test("error appears when folder can't be loaded", async ({ page }) => {
|
||||
await page.route(
|
||||
`**/v1/projects/${ridPrefix}${rid}/tree/${aliceMainHead}/markdown/`,
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
const modifiedFileFixture = `${projectFixtureUrl}/remotes/${bobRemote}/commits/2b32f6fe50090ebdb4cd7441e943330da3e6ff04`;
|
||||
|
||||
test("navigation from commit list", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(projectFixtureUrl);
|
||||
await page.getByTitle("Change peer").click();
|
||||
await page.locator("text=bob hyyzz9").click();
|
||||
@@ -18,6 +22,10 @@ test("navigation from commit list", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("relative timestamps", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.addInitScript(() => {
|
||||
window.initializeTestStubs = () => {
|
||||
window.e2eTestStubs.FakeTimers.install({
|
||||
@@ -34,6 +42,10 @@ test("relative timestamps", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("modified file", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(modifiedFileFixture);
|
||||
|
||||
// Commit header.
|
||||
@@ -59,6 +71,10 @@ test("modified file", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("created file", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(
|
||||
`${projectFixtureUrl}/remotes/${bobRemote}/commits/d6318f7f3d9c15b8ac6dd52267c53220d00f0982`,
|
||||
);
|
||||
@@ -69,6 +85,10 @@ test("created file", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("deleted file", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(
|
||||
`${projectFixtureUrl}/remotes/${bobRemote}/commits/cd13c2d9a8a930d64a82b6134b44d1b872e33662`,
|
||||
);
|
||||
|
||||
@@ -6,6 +6,10 @@ import {
|
||||
} from "@tests/support/fixtures.js";
|
||||
|
||||
test("peer and branch switching", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(projectFixtureUrl);
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
@@ -83,6 +87,10 @@ test("peer and branch switching", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("verified badge", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.goto(projectFixtureUrl);
|
||||
await page.locator('role=button[name="Commit count"]').click();
|
||||
|
||||
@@ -101,6 +109,10 @@ test("verified badge", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("relative timestamps", async ({ page }) => {
|
||||
if (process.env.HEARTWOOD) {
|
||||
test.skip();
|
||||
}
|
||||
|
||||
await page.addInitScript(() => {
|
||||
window.initializeTestStubs = () => {
|
||||
window.e2eTestStubs.FakeTimers.install({
|
||||
|
||||
BIN
Binary file not shown.
@@ -160,7 +160,11 @@ function log(text: string, label: string, outputLog: Stream.Writable) {
|
||||
}
|
||||
}
|
||||
|
||||
export function appConfigWithFixture() {
|
||||
export const appConfigWithFixture = process.env.HEARTWOOD
|
||||
? configFixtureHeartwood
|
||||
: configFixture;
|
||||
|
||||
export function configFixture() {
|
||||
window.APP_CONFIG = {
|
||||
walletConnect: {
|
||||
bridge: "https://radicle.bridge.walletconnect.org",
|
||||
@@ -181,15 +185,41 @@ export function appConfigWithFixture() {
|
||||
};
|
||||
}
|
||||
|
||||
export function configFixtureHeartwood() {
|
||||
window.APP_CONFIG = {
|
||||
walletConnect: {
|
||||
bridge: "https://radicle.bridge.walletconnect.org",
|
||||
},
|
||||
reactions: [],
|
||||
seeds: {
|
||||
pinned: [{ host: "0.0.0.0", emoji: "🚀" }],
|
||||
},
|
||||
projects: {
|
||||
pinned: [
|
||||
{
|
||||
name: "source-browsing",
|
||||
id: "rad:zKtT7DmF9H34KkvcKj9PHW19WzjT",
|
||||
seed: "0.0.0.0",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const aliceMainHead = "fcc929424b82984b7cbff9c01d2e20d9b1249842";
|
||||
export const aliceRemote =
|
||||
"hybg18bc4cu8z9xtj44skxperfdpxpp1wp8zygyzti5kfiggdizfxy";
|
||||
export const aliceRemote = process.env.HEARTWOOD
|
||||
? "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
: "hybg18bc4cu8z9xtj44skxperfdpxpp1wp8zygyzti5kfiggdizfxy";
|
||||
|
||||
export const bobRemote =
|
||||
"hyyzz9w4ffg16zftjki3enajm4mkqkayb5ch1p6ns3f83np1hqkrp6";
|
||||
export const rid = "hnrkdi8be7n4hhqoz9rpzrgd68u9dr3zsxgmy";
|
||||
export const ridPrefix = "rad:git:";
|
||||
export const rid = process.env.HEARTWOOD
|
||||
? "zKtT7DmF9H34KkvcKj9PHW19WzjT"
|
||||
: "hnrkdi8be7n4hhqoz9rpzrgd68u9dr3zsxgmy";
|
||||
export const ridPrefix = process.env.HEARTWOOD ? "rad:" : "rad:git:";
|
||||
export const projectFixtureUrl = `/seeds/0.0.0.0/${ridPrefix}${rid}`;
|
||||
export const seedPort = 8777;
|
||||
export const seedVersion = "0.2.0";
|
||||
export const seedRemote =
|
||||
"hybuytx44z9cfsm5739wecia9j4b7expgc15qkazph59szp57m4d3o";
|
||||
export const seedPort = process.env.HEARTWOOD ? 8080 : "8777";
|
||||
export const seedVersion = process.env.HEARTWOOD ? "0.1.0" : "0.2.0";
|
||||
export const seedRemote = process.env.HEARTWOOD
|
||||
? "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
: "hybuytx44z9cfsm5739wecia9j4b7expgc15qkazph59szp57m4d3o";
|
||||
|
||||
@@ -9,24 +9,30 @@ export default async function globalSetup(_config: FullConfig): Promise<void> {
|
||||
// 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 = seedRemote;
|
||||
|
||||
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";
|
||||
(process.env.HEARTWOOD
|
||||
? "👉 You can start it with `./scripts/run-httpd-with-fixtures`\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:${seedPort}`);
|
||||
const data = await response.json();
|
||||
peerId = data.peer.id;
|
||||
if (process.env.HEARTWOOD) {
|
||||
const response = await fetch(`http://0.0.0.0:${seedPort}/api`);
|
||||
const data = await response.json();
|
||||
peerId = data.node.id;
|
||||
} else {
|
||||
const response = await fetch(`http://0.0.0.0:${seedPort}`);
|
||||
const data = await response.json();
|
||||
peerId = data.peer.id;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw new Error(notRunningMessage);
|
||||
}
|
||||
|
||||
if (peerId !== palmTestFixtureSeedId) {
|
||||
if (peerId !== seedRemote) {
|
||||
const wrongSeedMessage = `The server on port ${seedPort} doesn't have the right fixtures.\n`;
|
||||
throw new Error(wrongSeedMessage + notRunningMessage);
|
||||
}
|
||||
|
||||
@@ -101,11 +101,11 @@ describe("routeToPath", () => {
|
||||
params: {
|
||||
view: { resource: "tree" },
|
||||
seed: "willow.radicle.garden",
|
||||
id: "rad:git:hnrkmg77m8tfzj4gi4pa4mbhgysfgzwntjpao",
|
||||
id: "rad:zKtT7DmF9H34KkvcKj9PHW19WzjT",
|
||||
},
|
||||
},
|
||||
output:
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrkmg77m8tfzj4gi4pa4mbhgysfgzwntjpao/tree",
|
||||
"/seeds/willow.radicle.garden/rad:zKtT7DmF9H34KkvcKj9PHW19WzjT/tree",
|
||||
description: "Seed Project Route",
|
||||
},
|
||||
])("$description", (route: any) => {
|
||||
@@ -219,8 +219,7 @@ describe("pathToRoute", () => {
|
||||
description: "registrations Submit Route",
|
||||
},
|
||||
{
|
||||
input:
|
||||
"/seeds/willow.radicle.garden/rad:git:hnrkmg77m8tfzj4gi4pa4mbhgysfgzwntjpao",
|
||||
input: "/seeds/willow.radicle.garden/rad:zKtT7DmF9H34KkvcKj9PHW19WzjT",
|
||||
output: {
|
||||
resource: "projects",
|
||||
params: {
|
||||
@@ -228,7 +227,7 @@ describe("pathToRoute", () => {
|
||||
seed: "willow.radicle.garden",
|
||||
profile: undefined,
|
||||
peer: undefined,
|
||||
id: "rad:git:hnrkmg77m8tfzj4gi4pa4mbhgysfgzwntjpao",
|
||||
id: "rad:zKtT7DmF9H34KkvcKj9PHW19WzjT",
|
||||
},
|
||||
},
|
||||
description: "Seed Project Route",
|
||||
|
||||
@@ -37,9 +37,15 @@ describe("Format functions", () => {
|
||||
});
|
||||
|
||||
test("formatRadicleId", () => {
|
||||
expect(
|
||||
utils.formatRadicleId("rad:git:hnrkemobagsicpf9sr95o3g551otspcd84c9o"),
|
||||
).toEqual("rad:git:hnrkem…d84c9o");
|
||||
if (process.env.HEARTWOOD) {
|
||||
expect(utils.formatRadicleId("rad:zKtT7DmF9H34KkvcKj9PHW19WzjT")).toEqual(
|
||||
"rad:zKtT7D…19WzjT",
|
||||
);
|
||||
} else {
|
||||
expect(
|
||||
utils.formatRadicleId("rad:git:hnrkemobagsicpf9sr95o3g551otspcd84c9o"),
|
||||
).toEqual("rad:git:hnrkem…d84c9o");
|
||||
}
|
||||
});
|
||||
|
||||
test("formatRadicleId throw when wrong ID", () => {
|
||||
@@ -117,7 +123,9 @@ describe("String Assertions", () => {
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ id: "rad:git:hnrkemobagsicpf9sr95o3g551otspcd84c9o", expected: true },
|
||||
process.env.HEARTWOOD
|
||||
? { id: "rad:zKtT7DmF9H34KkvcKj9PHW19WzjT", expected: true }
|
||||
: { id: "rad:git:hnrkemobagsicpf9sr95o3g551otspcd84c9o", expected: true },
|
||||
{ id: "0x1234567890123456789012345678901234567890", expected: false },
|
||||
])("isRadicleId $id => $expected", ({ id, expected }) => {
|
||||
expect(utils.isRadicleId(id)).toEqual(expected);
|
||||
|
||||
@@ -5,6 +5,16 @@ test.use({
|
||||
});
|
||||
|
||||
test("landing page", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.initializeTestStubs = () => {
|
||||
window.e2eTestStubs.FakeTimers.install({
|
||||
now: new Date("November 24 2022 12:00:00").valueOf(),
|
||||
shouldClearNativeTimers: true,
|
||||
shouldAdvanceTime: false,
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
await page.addInitScript(appConfigWithFixture);
|
||||
await page.goto("/", { waitUntil: "networkidle" });
|
||||
await expect(page).toHaveScreenshot();
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { test, expect } from "@tests/support/fixtures.js";
|
||||
|
||||
test("seed page", async ({ page }) => {
|
||||
await 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("/seeds/radicle.local", { waitUntil: "networkidle" });
|
||||
await expect(page).toHaveScreenshot();
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ function defineConstants() {
|
||||
const constants = {
|
||||
VITEST: process.env.VITEST !== undefined,
|
||||
PLAYWRIGHT: process.env.PLAYWRIGHT_TEST_BASE_URL !== undefined,
|
||||
HEARTWOOD: process.env.HEARTWOOD !== undefined,
|
||||
};
|
||||
|
||||
// Don't overwrite HASH_ROUTING in Playwright tests, so we can control it
|
||||
|
||||
Reference in New Issue
Block a user