Skip to content
This repository was archived by the owner on Jul 30, 2025. It is now read-only.

Commit 7d13a93

Browse files
myan9starpit
authored andcommitted
feat: add CommentaryReponse to snapshot/replay the response of commentary command
Fixes #5439
1 parent 7ba313a commit 7d13a93

File tree

9 files changed

+163
-48
lines changed

9 files changed

+163
-48
lines changed

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ export { disableInputQueueing } from './webapp/queueing'
147147
export { isPopup } from './webapp/popup-core'
148148
export { removeAllDomChildren as empty } from './webapp/util/dom'
149149
export { default as Presentation } from './webapp/views/presentation'
150+
export { CommentaryResponse, isCommentaryResponse } from './models/CommentaryResponse'
150151
export {
151152
ModeOrButton as Mode,
152153
Button,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2020 IBM Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Entity } from './entity'
18+
import REPL from './repl'
19+
20+
export type CommentaryResponse = {
21+
apiVersion: 'kui-shell/v1'
22+
kind: 'CommentaryResponse'
23+
props: {
24+
/** Content rendered inside the CardTitle */
25+
title?: string
26+
27+
/** Body of the Card. It will be passed through as the source <Markdown source="..." /> */
28+
children: string
29+
30+
/** [Optional] REPL controller, but required if you want your Card
31+
* to have functional kuiexec?command=... links via Markdown */
32+
repl?: REPL
33+
}
34+
}
35+
36+
export function isCommentaryResponse(entity: Entity): entity is CommentaryResponse {
37+
const response = entity as CommentaryResponse
38+
return response.apiVersion === 'kui-shell/v1' && response.kind === 'CommentaryResponse'
39+
}

packages/core/src/models/entity.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { UsageModel } from '../core/usage-error'
2121
import isMultiModalResponse from './mmr/is'
2222
import MultiModalResponse from './mmr/types'
2323
import { NavResponse, isNavResponse } from './NavResponse'
24+
import { CommentaryResponse } from './CommentaryResponse'
2425
import RadioTable from './RadioTable'
2526
import Presentation from '../webapp/views/presentation'
2627
import { ReactNode, isValidElement } from 'react'
@@ -182,7 +183,11 @@ export function isRawResponse<Content extends RawContent>(entity: Entity<Content
182183
* that views may wish to interpret into fancier views.
183184
*
184185
*/
185-
export type ScalarResponse<RowType extends Row = Row> = SimpleEntity | Table<RowType> | MixedResponse
186+
export type ScalarResponse<RowType extends Row = Row> =
187+
| SimpleEntity
188+
| Table<RowType>
189+
| MixedResponse
190+
| CommentaryResponse
186191

187192
export function isScalarResponse(response: Entity): response is ScalarResponse {
188193
return !isMultiModalResponse(response) && !isNavResponse(response)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2020 IBM Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as React from 'react'
18+
import { CommentaryResponse } from '@kui-shell/core'
19+
import Card from '../spi/Card'
20+
21+
export default class Commentary extends React.PureComponent<CommentaryResponse['props']> {
22+
public render() {
23+
return <Card {...this.props} />
24+
}
25+
}

plugins/plugin-client-common/src/components/Content/Scalar/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
isMessageWithCode,
2121
Tab as KuiTab,
2222
ScalarResponse,
23+
isCommentaryResponse,
2324
isHTML,
2425
isMarkdownResponse,
2526
isReactResponse,
@@ -29,6 +30,7 @@ import {
2930
isUsageError
3031
} from '@kui-shell/core'
3132

33+
import Commentary from '../Commentary'
3234
import HTMLDom from './HTMLDom'
3335
import renderTable from '../Table'
3436
import Markdown from '../Markdown'
@@ -83,6 +85,8 @@ export default class Scalar extends React.PureComponent<Props, State> {
8385
return <React.Fragment />
8486
} else if (typeof response === 'number' || typeof response === 'string') {
8587
return <pre>{response}</pre>
88+
} else if (isCommentaryResponse(response)) {
89+
return <Commentary {...response.props} />
8690
} else if (isRadioTable(response)) {
8791
return (
8892
<KuiContext.Consumer>

plugins/plugin-client-common/src/components/Views/Terminal/Block/Output.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import * as React from 'react'
1919
import {
2020
i18n,
2121
isCodedError,
22+
isCommentaryResponse,
2223
isHTML,
2324
isReactResponse,
2425
isMarkdownResponse,
@@ -199,6 +200,7 @@ export default class Output extends React.PureComponent<Props, State> {
199200
if (isFinished(block) && !isCancelled(block) && !isEmpty(block)) {
200201
const { response } = block
201202
return (
203+
isCommentaryResponse(response) ||
202204
isReactResponse(response) ||
203205
isHTML(response) ||
204206
isMarkdownResponse(response) ||

plugins/plugin-client-common/src/controller/card.ts

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {
18-
Arguments,
19-
Registrar,
20-
ParsedOptions,
21-
UsageModel,
22-
ReactResponse,
23-
findFile,
24-
expandHomeDir
25-
} from '@kui-shell/core'
26-
import { FStat } from '@kui-shell/plugin-bash-like/fs'
17+
import { Arguments, Registrar, ParsedOptions, UsageModel, ReactResponse } from '@kui-shell/core'
18+
2719
import card from '../components/spi/Card'
20+
import { fetchMarkdownFile } from './commentary'
2821

2922
/**
3023
* card command parsedOptions type
@@ -79,25 +72,8 @@ async function doCard(opts: Arguments<CardOptions>): Promise<ReactResponse> {
7972

8073
const filepath = option.filename || option.f
8174
if (filepath) {
82-
const fullpath = findFile(expandHomeDir(filepath))
83-
const suffix = filepath.substring(filepath.lastIndexOf('.') + 1)
84-
85-
if (suffix !== 'md') {
86-
throw new Error('File extension not support')
87-
} else {
88-
// fetch the data:
89-
// --with-data says give us the file contents
90-
const stats = (
91-
await opts.tab.REPL.rexec<FStat>(`vfs fstat ${opts.tab.REPL.encodeComponent(fullpath)} --with-data`)
92-
).content
93-
94-
if (stats.isDirectory) {
95-
throw new Error('Invalid filepath')
96-
} else {
97-
const data: string = stats.data
98-
return { react: card({ title, children: data, icon }) }
99-
}
100-
}
75+
const data = await fetchMarkdownFile(filepath, opts)
76+
return { react: card({ title, children: data, icon }) }
10177
} else {
10278
const body = argv.slice(1)
10379
if (body) {

plugins/plugin-client-common/src/controller/commentary.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,25 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Arguments, Registrar, UsageModel } from '@kui-shell/core'
17+
import {
18+
Arguments,
19+
CommentaryResponse,
20+
ParsedOptions,
21+
Registrar,
22+
UsageModel,
23+
findFile,
24+
expandHomeDir
25+
} from '@kui-shell/core'
26+
import { FStat } from '@kui-shell/plugin-bash-like/fs'
27+
28+
/**
29+
* commentary command parsedOptions type
30+
*/
31+
interface CommentaryOptions extends ParsedOptions {
32+
title: string
33+
file: string
34+
f: string
35+
}
1836

1937
/**
2038
* commentary command usage
@@ -38,9 +56,43 @@ const usage: UsageModel = {
3856
]
3957
}
4058

41-
async function addComment(args: Arguments) {
42-
// delegate to the card command
43-
return args.tab.REPL.qexec(args.command.replace(/^commentary/, 'card'))
59+
export async function fetchMarkdownFile(filepath: string, args: Arguments): Promise<string> {
60+
const fullpath = findFile(expandHomeDir(filepath))
61+
const suffix = filepath.substring(filepath.lastIndexOf('.') + 1)
62+
63+
if (suffix !== 'md') {
64+
throw new Error('File extension not support')
65+
} else {
66+
// fetch the data:
67+
// --with-data says give us the file contents
68+
const stats = (await args.tab.REPL.rexec<FStat>(`vfs fstat ${args.tab.REPL.encodeComponent(fullpath)} --with-data`))
69+
.content
70+
71+
if (stats.isDirectory) {
72+
throw new Error('Invalid filepath')
73+
} else {
74+
return stats.data as string
75+
}
76+
}
77+
}
78+
79+
async function addComment(args: Arguments<CommentaryOptions>): Promise<CommentaryResponse> {
80+
const { title } = args.parsedOptions
81+
const filepath = args.parsedOptions.file || args.parsedOptions.f
82+
83+
if (filepath) {
84+
const data = await fetchMarkdownFile(filepath, args)
85+
return {
86+
apiVersion: 'kui-shell/v1',
87+
kind: 'CommentaryResponse',
88+
props: {
89+
title,
90+
children: data
91+
}
92+
}
93+
} else {
94+
throw new Error('Insufficient arguments: must specify --file or -f')
95+
}
4496
}
4597

4698
/**

plugins/plugin-core-support/src/test/core-support/commentary.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,25 @@ import { Common, CLI, ReplExpect, Selectors } from '@kui-shell/test'
2121

2222
const ROOT = dirname(require.resolve('@kui-shell/plugin-core-support/package.json'))
2323

24-
describe('commentary command', function(this: Common.ISuite) {
24+
describe('commentary and replay', function(this: Common.ISuite) {
2525
before(Common.before(this))
2626
after(Common.after(this))
2727

28+
const verifyComment = async () => {
29+
await this.app.client.waitForVisible(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD}`)
30+
const title: string = await this.app.client.getText(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD_TITLE}`)
31+
assert.strictEqual(title, 'hello there')
32+
33+
const head1: string = await this.app.client.getText(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD} h1`)
34+
assert.strictEqual(head1, 'The Kui Framework for Graphical Terminals')
35+
36+
const head2: string = await this.app.client.getText(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD} h2`)
37+
assert.strictEqual(head2, 'Installation')
38+
}
2839
const addComment = () => {
2940
it('should show comment with file', () =>
3041
CLI.command(`commentary --title "hello there" -f=${ROOT}/tests/data/comment.md`, this.app)
31-
.then(async () => {
32-
await this.app.client.waitForVisible(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD}`)
33-
const title: string = await this.app.client.getText(
34-
`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD_TITLE}`
35-
)
36-
assert.strictEqual(title, 'hello there')
37-
38-
const head1: string = await this.app.client.getText(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD} h1`)
39-
assert.strictEqual(head1, 'The Kui Framework for Graphical Terminals')
40-
41-
const head2: string = await this.app.client.getText(`${Selectors.OUTPUT_LAST} ${Selectors.TERMINAL_CARD} h2`)
42-
assert.strictEqual(head2, 'Installation')
43-
})
42+
.then(() => verifyComment())
4443
.catch(Common.oops(this)))
4544
}
4645

@@ -50,4 +49,16 @@ describe('commentary command', function(this: Common.ISuite) {
5049
.then(ReplExpect.okWithCustom({ expect: Common.expectedVersion }))
5150
.catch(Common.oops(this)))
5251
addComment()
52+
53+
it('should snapshot', () =>
54+
CLI.command('snapshot /tmp/test.kui', this.app)
55+
.then(ReplExpect.justOK)
56+
.catch(Common.oops(this, true)))
57+
58+
it('should refresh', () => Common.refresh(this))
59+
60+
it('should replay', () =>
61+
CLI.command('replay /tmp/test.kui', this.app)
62+
.then(() => verifyComment())
63+
.catch(Common.oops(this)))
5364
})

0 commit comments

Comments
 (0)