Skip to content

Commit

Permalink
feat(docs): add missing API's examples
Browse files Browse the repository at this point in the history
  • Loading branch information
jlenon7 committed Oct 16, 2023
1 parent 8b28afe commit 2d90f72
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 45 deletions.
4 changes: 2 additions & 2 deletions docs/architecture-concepts/application-lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ The Kernel is also responsible for registering your commands defined in your
`.athennarc.json` file. By default, Athenna will always use the default
implementation `ConsoleKernel` class imported from `@athenna/http` package. If
you prefer, you can create your custom Kernel implementation, extending the
default ConsoleKernel class and registering it in your `Ignite.artisan` method
default ConsoleKernel class and registering it in your `Ignite.console()` method
call:

```typescript
Expand All @@ -283,7 +283,7 @@ const ignite = await new Ignite().load(import.meta.url, {
bootLogs: false
})

await ignite.artisan({ kernelPath: '#app/http/CustomKernel' })
await ignite.console({ kernelPath: '#app/http/CustomKernel' })
```

### Execution
Expand Down
25 changes: 23 additions & 2 deletions docs/cli-application/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ sticker.render()

Create a task runner that will log the
status of each task. The task
method is very useful when you need to
method is beneficial when you need to
do a lot of tasks in order, giving a status
to all the tasks and how much time it has
taken to execute:
Expand All @@ -961,6 +961,27 @@ task.add('Second task', async task => {
await task.run()
```

You can also use the `task.addPromise()` method to don't
mind about calling `task.complete()` or `task.fail()` method.
This method will automatically call it for you if the promise
resolves or rejects:

```typescript
import { Exec } from '@athenna/common'

const task = this.logger.task()

task.addPromise('First task', async () => {
await Exec.sleep(1000)
})

task.addPromise('Second task', async () => {
await Exec.sleep(1000)
})

await task.run()
```

## Generating templates in commands

Artisan has support to generate files from templates.
Expand Down Expand Up @@ -1052,7 +1073,7 @@ that is exactly what we do internally with other
#### `this.generator.path()`

Set the file path where the file will be generated.
Rememeber that the file name in the path will be used
Remember that the file name in the path will be used
to define the name properties:

```typescript
Expand Down
2 changes: 1 addition & 1 deletion docs/cli-application/error-handling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Now you need to register your exception handler when
bootstrapping your application:

```typescript title="Path.bootstrap('main.ts')"
await ignite.artisan(process.argv, {
await ignite.console(process.argv, {
displayName: 'Athenna',
exceptionHandlerPath: '#app/console/exceptions/Handler', 👈
})
Expand Down
2 changes: 1 addition & 1 deletion docs/cli-application/publishing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ you can publish a global CLI application in NPM registry.

To publish your package on the NPM registry, you need to
have an account at NPM. If you don't have an account,
visit the [NPM sign up page](https://www.npmjs.com/signup)
visit the [NPM sign-up page](https://www.npmjs.com/signup)
to create one.

After creating the account, open your terminal and run the
Expand Down
20 changes: 9 additions & 11 deletions docs/cli-application/running.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ default in your application, we are going to focus in

## Registering your CLI command name

To register your CLI command name you need to add the
To register your CLI command name, you need to add the
`bin` object inside your `package.json` file and set the
path to the entry point file of your CLI:

Expand All @@ -34,17 +34,15 @@ path to the entry point file of your CLI:

In our example we defined the `./bootstrap/main.js`
file as the entrypoint file of our CLI. By default, this
file comes with the shebang line
`#!/usr/bin/env -S node --experimental-import-meta-resolve`
file comes with the shebang line `#!/usr/bin/env node`
in the top of the file. Without this line the `npm link`
command will not work. So just in case you want to define a
different entrypoint file, remember that
`#!/usr/bin/env -S node --experimental-import-meta-resolve`
different entrypoint file, remember that `#!/usr/bin/env node`
should be on the top of this file.

## Linking the CLI

Now you just need to run the following command in your project root:
Now you need to run the following command in your project root:

```shell
npm link
Expand All @@ -63,31 +61,31 @@ name will be rendered in the terminal using
[chalk-rainbow](https://www.npmjs.com/package/chalk-rainbow)
and [figlet](https://www.npmjs.com/package/figlet).

By default Artisan always display the `Artisan` name, but you can
By default, Artisan always display the `Artisan` name, but you can
change it for your own display name by setting the `displayName`
property in `Ignite.artisan()` method:
property in `Ignite.console()` method:

```typescript title="Path.bootstrap('main.ts')"
import { Ignite } from '@athenna/core'

const ignite = await new Ignite().load(import.meta.url)

await ignite.artisan({
await ignite.console({
displayName: 'Your CLI Command', 👈
})
```

:::tip

If you wish to disable the display name, just set the `displayName`
If you wish to disable the display name, set the `displayName`
as `null`:

```typescript title="Path.bootstrap('main.ts')"
import { Ignite } from '@athenna/core'

const ignite = await new Ignite().load(import.meta.url)

await ignite.artisan({
await ignite.console({
displayName: null, 👈
})
```
Expand Down
99 changes: 87 additions & 12 deletions docs/digging-deeper/repl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,62 @@ commands available.

:::

## Extending REPL Context

When running your REPL session, it could be painful to
keep declaring the same variables over and over again. To
avoid that you can use the `repl.setIntContext()` method,
and it will be available in all your REPL sessions:

```typescript title="Path.bootstrap('repl.ts')"
import { Ignite } from '@athenna/core'

const ignite = await new Ignite().load(import.meta.url, { bootLogs: false })

const repl = await ignite.repl()

repl.setInContext('helloWorld', 'Hello World!')
repl.setInContext('appService', ioc.safeUse('App/Services/AppService'))
```

## Custom REPL commands

You can create custom REPL commands like `.ls` and `.clean`
to make your life easier when running your REPL session:

```typescript title="Path.bootstrap('repl.ts')"
import { Ignite } from '@athenna/core'

const ignite = await new Ignite().load(import.meta.url, { bootLogs: false })

const repl = await ignite.repl()

repl
.command('greet')
.help('Greet someone')
.action(function action(name: string) {
this.clearBufferedCommand()
console.log(`Hello ${name}!`)
this.displayPrompt()
})
.register()
```

Now the `.greet` command will always be available in your
REPL sessions:

```shell
.greet Antoine
> Hello Antoine!
```

## Pre-importing modules

You can pre-import modules in your REPL session to avoid keep
importing them everytime inside your REPL session. You can do that
in your `Path.bootstrap('repl.ts')` file:
### Pre-importing module

Like declaring variables in the REPL context, it could be painful
to import modules in your everytime in your REPL sessions. To avoid
it you can use the `repl.import()` method:

```typescript
import { Ignite } from '@athenna/core'
Expand All @@ -78,19 +129,43 @@ const ignite = await new Ignite().load(import.meta.url, { bootLogs: false })

const repl = await ignite.repl()

await import('@athenna/common').then(common => {
Object.keys(common).forEach(key => (
repl.context[key] = common[key] 👈
))
})
await repl.import('common', '@athenna/common')
```

Now you are able to use the `common` variable in your REPL sessions:

repl.context.helloWorld = 'Hello World!' 👈
repl.context.appService = ioc.safeUse('App/Services/AppService') 👈
```typescript
common.Path.pwd()
```

### Pre-importing modules names as keys

Sometimes a module exports a variety of classes, functions, etc.
Like `@athenna/common`, that exports a variety of helpers, and would
be easier if we could directly use the helper name instead
of the `common` variable as we have created in the example above.
With `repl.importAll()` you can import the module names as keys to
the REPL context:

```typescript
import { Ignite } from '@athenna/core'

const ignite = await new Ignite().load(import.meta.url, { bootLogs: false })

const repl = await ignite.repl()

await repl.importAll('@athenna/common')
```

Now you are able to use the `Path` helper directly in your REPL sessions:

```typescript
Path.pwd()
```

:::info

As you can see in the implementation above, by default Athenna
pre-imports all the `@athenna/common` helpers to your REPL session.
By default, your `Path.bootstrap('repl.ts')` file already
imports all the `@athenna/common` helpers to your REPL session 🤩.

:::
72 changes: 67 additions & 5 deletions docs/testing/cli-tests.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ assertions for inspecting the output.

The `command` property in your test context will only be
available if you register the command plugin within the
`Runner` class. By default your Athenna application already
`Runner` class. By default, your Athenna application already
comes with the command plugin registered. But we are
going to cover how to register it manually if needed.

Just call the `Runner.addPlugin()` static method to setup
Just call the `Runner.addPlugin()` static method to set up
the request plugin imported from `@athenna/artisan/testing/plugins`:

```typescript title="Path.bootstrap('test.ts')"
Expand Down Expand Up @@ -88,9 +88,11 @@ export default class ExampleTest {

## Changing Artisan file path

### Changing the default Artisan file path

As mentioned previously, the `command.run()` method invokes
a child process using the `Path.bootstrap('artisan.ts')` file.
But for some reason you may want to change which file should be
But for some reason, you may want to change which file should be
used to test your commands. To do so, you can call the
`TestCommand.setArtisanPath()` static method before running
your tests:
Expand All @@ -114,6 +116,62 @@ await Runner.setTsEnv()
.run()
```

### Changing Artisan file path per command

When running your tests, you may want to create a different behavior
for a specific command, like mocking the prompts to not block your test
execution or adding some different value for an `Env` or `Config`.

Since the `command.run()` method invokes a child process, you can't do
this kind of customization in your tests:

```typescript
import { Config } from '@athenna/config'
import { Test, type Context } from '@athenna/test'

export default class ExampleTest {
@Test()
public async testConfigCommand({ command }: Context) {
Config.set('app.name', 'MyAppName')

const output = await command.run('greet')

output.assertLogged('Hello from MyAppName!') //
}
```
To solve this problem, you can use a different `artisan` file
for each `command.run()` call. Let's first create a new `artisan`
file and save it in our `fixtures` path:
```typescript title="Path.fixtures('consoles/artisan-set-app-name.ts')"
import { Ignite } from '@athenna/core'
import { Config } from '@athenna/config'

const ignite = await new Ignite().load(import.meta.url, { bootLogs: false })

Config.set('app.name', 'MyAppName')

await ignite.console(process.argv, { displayName: 'Artisan' })
```
Now, we can use this new `artisan` file to run our command:
```typescript
import { Path } from '@athenna/common'
import { Test, type Context } from '@athenna/test'

export default class ExampleTest {
@Test()
public async testConfigCommand({ command }: Context) {
const output = await command.run('greet', {
path: Path.fixtures('consoles/artisan-set-app-name.ts') 👈
})

output.assertLogged('Hello from MyAppName!') //
}
```
## Debugging outputs
After executing a test command to your application,
Expand All @@ -126,7 +184,7 @@ import { Test, type Context } from '@athenna/test'
export default class ExampleTest {
@Test()
public async testBasicCommand({ command }: Context) {
const output = await command.run('/')
const output = await command.run('basic')

console.log(output.output.stdout)
console.log(output.output.stderr)
Expand Down Expand Up @@ -189,6 +247,7 @@ Assert the command has logged the expected message:

```typescript
output.assertLogged('Hello World!')
output.assertNotLogged('Hello World!')
```

This method validates that the log message will be
Expand All @@ -198,6 +257,7 @@ argument:

```typescript
output.assertLogged('Hello World!', 'stdout') // or stderr
output.assertNotLogged('Hello World!', 'stdout') // or stderr
```

#### `assertLogMatches()`
Expand All @@ -208,13 +268,15 @@ provided:

```typescript
output.assertLogMatches(/Hello World/)
output.assertLogNotMatches(/Hello World/)
```

This method validates that the regex will match in `stdout`
or `stderr`. To force the stream type
where this log should match you can set it as second
where this log should match, you can set it as second
argument:

```typescript
output.assertLogMatches(/Hello World/, 'stdout') // or stderr
output.assertLogNotMatches(/Hello World/, 'stdout') // or stderr
```
Loading

0 comments on commit 2d90f72

Please sign in to comment.