Skip to content

Commit

Permalink
rework init (#457)
Browse files Browse the repository at this point in the history
* rework init

* readd config

* change default testcases

* add schema to config

* update docs
  • Loading branch information
timbrinded authored Feb 3, 2025
1 parent 626e860 commit bfaa04e
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 66 deletions.
7 changes: 7 additions & 0 deletions .changeset/khaki-cycles-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@moonwall/types": minor
"@moonwall/cli": minor
"@moonwall/docs": minor
---

Updated `init` command
26 changes: 26 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ jobs:
cache: "pnpm"
- run: pnpm install
- run: pnpm run fmt

test_init:
runs-on: ubuntu-latest
needs: ["build"]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9.1.4
- uses: actions/setup-node@v4
with:
node-version: 23
cache: "pnpm"
- name: Build like before
run: |
pnpm install
pnpm run build
- name: Initialize config
run: |
cd test
rm moonwall.config.json
pnpm moonwall init --acceptAllDefaults
- name: Run test
run: |
cd test
pnpm moonwall test default_env
test_basic:
runs-on: ubuntu-latest
Expand Down
81 changes: 71 additions & 10 deletions docs/guide/cmd/init.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,77 @@
# Init

The moonwall `init` command walks you through a step-by-step process to setting up your `moonwall.config.json` file.
The `init` command guides you through a step-by-step process to create a configuration file for Moonwall. This command will set up a new `moonwall.config.json` file in your current directory—provided one does not already exist.

The command takes no parameters but you'll be asked for the following to complete the setup of your `moonwall.config.json` file. Press `enter` at each step to accept the default parameters or enter your own at each step. These parameters and their default values are listed below:
When you run:

- Label: *moonwall_config*
- Global timeout value: *30000*
- Environment Name: *default_env*
- Path for where tests for this environment are kept: *tests/*
- Network Foundation: *dev*
- Would you like to generate this config? (no to restart from beginning) (Y/n) *Y*
`pnpm moonwall init`

For more details about configuring your `Moonwall.config` see the [Quick Start Guide](/guide/test/quick-start).
::: tip
You can provide the option `--acceptAllDefaults` to YOLO the defaults and save a config file immediately.
:::

![Moonwall init terminal screenshot](/init.png)
you will be prompted to enter a few key configuration values. Press `Enter` at each prompt to accept the default value or type your own. The prompts include:

- **Label:** The name for your config file.
_Default: `moonwall_config`_

- **Global Timeout:** The default timeout for tests (in milliseconds).
_Default: `30000`_

- **Environment Name:** The name of the Moonwall environment you want to create first (you can always make as many new environments as you like).
_Default: `default_env`_

- **Test Directory:** The path where tests for this environment are stored.
_Default: `tests/default/`_

::: info
By default, the `foundation` is set to `dev` but you can change it to `chopsticks`, `zombie`, or `read_only` as required.
:::

After enter and confirming these values, the following will happen:

- Creates the directories `scripts`, `tests`, and `tmp` (if they don’t already exist).
- Generates a new `moonwall.config.json` file with your specified settings.
- Writes a sample test file into your test directory to help you get started.

## Generated Configuration Example

Below is an example of the generated `moonwall.config.json`:

```json
{
"$schema": "https://raw.githubusercontent.com/Moonsong-Labs/moonwall/main/packages/types/config_schema.json",
"label": "moonwall_config",
"defaultTestTimeout": 30000,
"environments": [
{
"name": "default_env",
"testFileDir": ["tests/default/"],
"multiThreads": false,
"foundation": {
"type": "dev",
"launchSpec": [
{
"name": "moonbeam",
"useDocker": true,
"newRpcBehaviour": true,
"binPath": "moonbeamfoundation/moonbeam"
}
]
}
}
]
}
```

After initialization, a sample test file is created in the specified test directory so you can immediately begin writing tests for your local dev environment.

The tests should run successfully with default settings, as it will use a moonbeam docker image to spin up a local dev environment. You can modify this environment to whichever substrate node binary location or docker as you like.

::: info
If a `moonwall.config.json` file already exists, the init command will abort to prevent overwriting your configuration. Remove or rename the existing file if you wish to run the init process again.
:::

For more details about configuration options and further customization, please see the [Quick Start Guide](/guide/test/quick-start).

![Moonwall init terminal screenshot](/init.png)
Binary file modified docs/public/init.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 13 additions & 3 deletions packages/cli/src/cmds/entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,19 @@ yargs(hideBin(process.argv))
default: "moonwall.config.json",
},
})
.command("init", "Run tests for a given Environment", async () => {
await generateConfig();
})
.command(
"init",
"Run tests for a given Environment",
(yargs) =>
yargs.option("acceptAllDefaults", {
type: "boolean",
description: "Accept all defaults",
alias: "A",
}),
async (argv) => {
await generateConfig(argv);
}
)
.command<fetchArtifactArgs>(
"download <bin> [ver] [path]",
"Download x86 artifact from GitHub",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/cmds/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async function mainMenu(config?: MoonwallConfig) {

switch (menuChoice) {
case "init":
await generateConfig();
await generateConfig({});
await createFolders();
return false;
case "run": {
Expand Down
189 changes: 140 additions & 49 deletions packages/cli/src/internal/cmdFunctions/initialisation.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,91 @@
import type { FoundationType, MoonwallConfig } from "@moonwall/types";
import fs from "node:fs/promises";
import { input, number, confirm } from "@inquirer/prompts";
import { input, number, confirm, select } from "@inquirer/prompts";

export async function createFolders() {
await fs.mkdir("scripts").catch(() => "scripts folder already exists, skipping");
await fs.mkdir("tests").catch(() => "tests folder already exists, skipping");
await fs.mkdir("tmp").catch(() => "tmp folder already exists, skipping");
}

export async function generateConfig() {
for (;;) {
if (await fs.access("moonwall.config.json").catch(() => true)) {
const label = await input({
message: "Provide a label for the config file",
default: "moonwall_config",
});

const timeout = await number({
message: "Provide a global timeout value",
default: 30000,
});
export async function generateConfig(argv: { acceptAllDefaults?: boolean }) {
interface ConfigAnswers {
label: string;
timeout: number;
environmentName: string;
foundation: FoundationType;
useDocker?: boolean;
testDir: string;
}

const environmentName = await input({
message: "Provide a name for this environment",
default: "default_env",
});
let answers: ConfigAnswers;

const foundation = (await input({
message: "What type of network foundation is this?",
default: "dev",
})) as FoundationType;
try {
await fs.access("moonwall.config.json");
console.log("ℹ️ Config file already exists at this location. Quitting.");
return;
} catch (_) {
// File does not exist — proceed with configuration
}

const testDir = await input({
message: "Provide the path for where tests for this environment are kept",
default: "tests/",
});
if (argv.acceptAllDefaults) {
answers = {
label: "moonwall_config",
timeout: 30000,
environmentName: "default_env",
foundation: "dev",
testDir: "tests/default/",
};
} else {
while (true) {
answers = {
label: await input({
message: "Provide a label for the config file",
default: "moonwall_config",
}),
timeout:
(await number({
message: "Provide a global timeout value",
default: 30000,
})) ?? 30000,
environmentName: await input({
message: "Provide a name for this environment",
default: "default_env",
}),
foundation: "dev",
testDir: await input({
message: "Provide the path for where tests for this environment are kept",
default: "tests/default/",
}),
};

const proceed = await confirm({
message: "Would you like to generate this config? (no to restart from beginning)",
});

if (proceed === false) {
continue;
if (proceed) {
break;
}

const JSONBlob = JSON.stringify(
createConfig({
label,
timeout: timeout ?? 30000,
environmentName,
foundation,
testDir,
}),
null,
3
);

await fs.writeFile("moonwall.config", textBlob + JSONBlob, "utf-8");
process.env.MOON_CONFIG_PATH = "./moonwall.config";
break;
console.log("Restarting the configuration process...");
}
console.log("ℹ️ Config file already exists at this location. Quitting.");
return;
}

const config = createSampleConfig({
label: answers.label,
timeout: answers.timeout,
environmentName: answers.environmentName,
foundation: answers.foundation,
testDir: answers.testDir,
});

const JSONBlob = JSON.stringify(config, null, 3);
await fs.writeFile("moonwall.config.json", JSONBlob, "utf-8");
process.env.MOON_CONFIG_PATH = "./moonwall.config.json";
await createSampleTest(answers.testDir);
console.log("Test directory created at: ", answers.testDir);
console.log(
`You can now add tests to this directory and run them with 'pnpm moonwall test ${answers.environmentName}'`
);
console.log("Goodbye! 👋");
}

Expand All @@ -88,8 +111,76 @@ export function createConfig(options: {
};
}

const textBlob = `// This Moonwall Config file should be modified to include all types
// of environments you wish to test against.
export function createSampleConfig(options: {
label: string;
timeout: number;
environmentName: string;
foundation: FoundationType;
testDir: string;
}): MoonwallConfig {
return {
$schema:
"https://raw.githubusercontent.com/Moonsong-Labs/moonwall/main/packages/types/config_schema.json",
label: options.label,
defaultTestTimeout: options.timeout,
environments: [
{
name: options.environmentName,
testFileDir: [options.testDir],
multiThreads: false,
foundation: {
type: "dev",
launchSpec: [
{
name: "moonbeam",
useDocker: true,
newRpcBehaviour: true,
binPath: "moonbeamfoundation/moonbeam",
},
],
},
},
],
};
}

async function createSampleTest(directory: string) {
await fs.mkdir(directory, { recursive: true });
await fs.writeFile(`${directory}/sample.test.ts`, sampleTest, "utf-8");
}

const sampleTest = `import { describeSuite, expect } from "@moonwall/cli";
describeSuite({
id: "B01",
title: "Sample test suite for moonbeam network",
foundationMethods: "dev",
testCases: ({ context, it }) => {
const ALITH_ADDRESS = "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"
it({
id: "T01",
title: "Test that API is connected correctly",
test: async () => {
const chainName = context.pjsApi.consts.system.version.specName.toString();
const specVersion = context.pjsApi.consts.system.version.specVersion.toNumber();
expect(chainName.length).toBeGreaterThan(0)
expect(chainName).toBe("moonbase")
expect(specVersion).toBeGreaterThan(0)
},
});
it({
id: "T02",
title: "Test that chain queries can be made",
test: async () => {
const balance = (await context.pjsApi.query.system.account(ALITH_ADDRESS)).data.free
expect(balance.toBigInt()).toBeGreaterThan(0n)
},
});
},
});
// For more information on how to configure Moonwall, please visit:
// https://moonsong-labs.github.io/moonwall/config/intro.html\n`;
`;
4 changes: 1 addition & 3 deletions packages/cli/src/internal/commandParsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ export class LaunchCommandParser {
this.cmd = launchSpec.binPath;
this.args = launchSpec.options
? [...launchSpec.options]
: launchSpec.useDocker
? []
: fetchDefaultArgs(path.basename(launchSpec.binPath), additionalRepos);
: fetchDefaultArgs(path.basename(launchSpec.binPath), additionalRepos);
}

private overrideArg(newArg: string): void {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/lib/repoDefinitions/moonbeam.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const repo: RepoSpec = {
"--no-telemetry",
"--reserved-only",
"--rpc-cors=all",
"--unsafe-rpc-external",
"--unsafe-force-node-key-generation",
"--no-grandpa",
"--sealing=manual",
Expand Down
Loading

0 comments on commit bfaa04e

Please sign in to comment.