Skip to content

Commit

Permalink
better testing UI
Browse files Browse the repository at this point in the history
  • Loading branch information
marstamm committed Aug 30, 2024
1 parent a1413b1 commit 22499d8
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 49 deletions.
140 changes: 121 additions & 19 deletions client/src/app/tabs/robot/BottomPanel/Run/RobotOutputTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,49 @@
* except in compliance with the MIT License.
*/

import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { Fill } from '../../../../slot-fill';
import { Modal } from '../../../../../shared/ui';

import { RunButton } from './RunButton';

import './RobotOutputTab.less';
import { Button, CodeSnippet, CodeSnippetSkeleton, Column, FlexGrid, Grid, Heading, Row, Section, Tile } from '@carbon/react';
import { Button, CodeSnippet, CodeSnippetSkeleton, Column, Grid, Heading, Layer, Section, Stack, TextInput, Tile, Form, TextArea, FlexGrid } from '@carbon/react';

Check failure on line 16 in client/src/app/tabs/robot/BottomPanel/Run/RobotOutputTab.js

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest)

'FlexGrid' is defined but never used. Allowed unused vars must match /^_/u

import './carbon.scss';
import { useLocalState } from '../useLocalState';
import { runFile } from '../Deployment/API';
import useAsyncMemo from '../useAsyncMemo';

export default function RobotOutputTab(props) {
const {
layout = {}
layout = {},
id
} = props;

const [ output, setOutput ] = useLocalState(id + 'output', '');
const [ isRunning, setIsRunning ] = useState(false);


return <>
<Fill slot="bottom-panel"
id="robot-output"
label="Robot Testing"
layout={ layout }
priority={ 15 }
actions={ [
{
icon: () => <RunButton { ...props } />,
title: 'Run robot script',
onClick: () => {},
}
]
}
>
<Section className="robotOutput" style={ { height: '100%', padding: '10px' } }>
<Section>
<Content { ...props } />
<Grid fullWidth={ true }>
<Column span={ 4 }>
<Tile>
<Layer>
<CarbonRunForm { ...props } setOutput={ setOutput } isRunning={ isRunning } setIsRunning={ setIsRunning } />
</Layer>
</Tile>
</Column>
<Column span={ 12 }>
<Content { ...props } output={ output } isRunning={ isRunning } />
</Column>
</Grid>
</Section>
</Section>
</Fill>
Expand All @@ -56,6 +65,8 @@ function Content(props) {
isRunning
} = props;

console.log('isRunning', isRunning);

if (isRunning) {
return <RobotReport output={ {} } />;
}
Expand All @@ -74,21 +85,25 @@ function RobotReport(props) {

const [ showReport, setShowReport ] = useState(false);

console.log(output);

return <Grid condensed={ true }>
<Column sm="75%" md="75%" lg="75%">
<Column lg={ 8 } md={ 4 } sm={ 4 }>
<Tile>
{/* <Stack gap={ 3 }> */}
<Heading>Output</Heading>
{output.stdOut ? <CodeSnippet type="multi">{output.stdOut}</CodeSnippet> : <CodeSnippetSkeleton type="multi" />}
{output.stdOut ?
<code type="multi" className="text-mono"><pre style={ { overflow: 'auto', userSelect: 'text' } }>{output.stdOut}</pre></code> :
<CodeSnippetSkeleton type="multi" />}
{output.log && <Button onClick={ () => setShowReport('log') }>Show Log</Button>}
{showReport && <Report content={ output.log } onClose={ () => setShowReport(false) } />}
{/* </Stack> */}
</Tile>
</Column>
<Column sm="25%" md="25%" lg="25%">
<Column lg={ 4 } md={ 4 } sm={ 4 }>
<Tile>
{/* <Stack gap={ 3 }> */}
<Heading>Variables</Heading>
{output.variables ? <CodeSnippet type="multi">{JSON.stringify(output.variables, null, 2)}</CodeSnippet> : <CodeSnippetSkeleton type="multi" />}
{/* </Stack> */}
</Tile>
</Column>
</Grid> ;
Expand Down Expand Up @@ -119,3 +134,90 @@ function Report(props) {
);

}


function CarbonRunForm(props) {

const {
getValue,
name,
setIsRunning,
isRunning,
setOutput,
id
} = props;

const [ values, setValues ] = useLocalState(id + 'robotTab', {
'name': name?.split('.')?.[0],
'endpoint': 'http://localhost:36227/',
'variables': ''
});

const onSubmit = async (...rest) => {
setIsRunning(true);
const response = await runFile({
...values,
script: getValue()
});
setIsRunning(false);
setOutput(response);
};

const jsonValid = useMemo(() => {
const value = values.variables;
if (value && value.trim().length > 0) {
try {
JSON.parse(value);
} catch (e) {
return true;
}
return false;
}
}, [ values.variables ]);

const endpointValid = useAsyncMemo(() => {
return validateEndpointURL(values.endpoint);
}, [ values.endpoint ], false);

return <Form>
<Stack gap={ 3 }>
<TextInput
id="ScriptName"
labelText="Script Name"
value={ values.name }
onChange={ e => setValues({ ...values, name: e.target.value }) }
/>
<TextInput
id="endpoint"
labelText="Endpoint URL"
value={ values.endpoint }
onChange={ e => setValues({ ...values, endpoint: e.target.value }) }
invalidText="Could not connect to RPA runtine. Make sure the RPA runtime is running."
invalid={ !!endpointValid }
/>
<TextArea
rows="3"
id="variables"
labelText="Variables"
placeholder="A JSON string representing the variables the script will be called with"
helperText={ <span>Must be a proper <a href="https://www.w3schools.com/js/js_json_intro.asp">JSON string</a> representing <a href="https://docs.camunda.io/docs/components/concepts/variables/?utm_source=modeler&utm_medium=referral">Zeebe variables</a>.</span> }
value={ values.variables }
onChange={ e => setValues({ ...values, variables: e.target.value }) }
invalidText="Variables is not valid JSON"
invalid={ !!jsonValid }
/>
<Button onClick={ onSubmit } disabled={ isRunning }>Run Script</Button>
</Stack>
</Form>;
}


const validateEndpointURL = async (value) => {
try {
const response = await fetch(value + 'status');
console.log(response);
} catch (error) {
console.error(error);
return 'Could not connect to RPA runtine. Make sure the RPA runtime is running.';
}
};
43 changes: 20 additions & 23 deletions client/src/app/tabs/robot/BottomPanel/Run/RunButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,43 +18,40 @@ import * as css from './DeploymentPlugin.less';
import { Fill } from '../../../../slot-fill';
import { Overlay } from '../../../../../shared/ui';
import RunForm from './RunForm';
import { Chemistry } from '@carbon/react/icons';


export default function RunButtonFill(props) {

const [ cachedValues, setCachedValues ] = useState({});
const [ isOpen, setIsOpen ] = useState(false);
const {
layout,
onAction
} = props;

const buttonRef = useRef();

const onClose = () => {
setIsOpen(false);
const { panel = {} } = layout;

const onToggle = () => {

if (!panel.open || panel.tab !== 'robot-output') {
onAction('open-panel', { tab: 'robot-output' });
} else if (panel.tab === 'robot-output') {
onAction('close-panel');
}
};

return <>
<Fill slot="status-bar__file" group="9_deploy" priority={ 2 }>
<Fill slot="status-bar__file" group="8_deploy" priority={ 10 }>
<button
ref={ buttonRef }
onClick={ () => setIsOpen(!isOpen) }
title="Run robot script"
className={ classNames('btn', css.DeploymentPlugin, { 'btn--active': isOpen }) }
onClick={ () => onToggle() }
title="Test ROBOT script"
className={ classNames('btn', css.DeploymentPlugin, { 'btn--active': panel.open && panel.tab === 'robot-output' }) }
>
<RunIcon className="icon" />
<Chemistry />
</button>
</Fill>

{ isOpen &&
<Overlay
onClose={ onClose }
anchor={ buttonRef.current }
>
<RunForm
cachedValues={ cachedValues }
setCachedValues={ setCachedValues }
onClose={ onClose }
{ ...props }
/>
</Overlay>
}
</>;
}

Expand Down
2 changes: 1 addition & 1 deletion client/src/app/tabs/robot/BottomPanel/Run/RunForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
import { runFile } from '../Deployment/API';
import { useLocalState } from '../useLocalState';

export default function DeploymentForm(props) {
export default function RunForm(props) {
const {
onClose,
getValue,
Expand Down
8 changes: 2 additions & 6 deletions client/src/app/tabs/robot/BottomPanel/Run/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@ import RobotOutputTab from './RobotOutputTab';

export default function(props) {

const [ output, setOutput ] = useState('');
const [ isRunning, setIsRunning ] = useState(false);


return <>
<RobotOutputTab { ...props } setOutput={ setOutput } setIsRunning={ setIsRunning } output={ output } isRunning={ isRunning }></RobotOutputTab>
<RunButton setOutput={ setOutput } setIsRunning={ setIsRunning } { ...props }></RunButton>
<RobotOutputTab { ...props }></RobotOutputTab>
<RunButton { ...props }></RunButton>
</>;

}
21 changes: 21 additions & 0 deletions client/src/app/tabs/robot/BottomPanel/useAsyncMemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership.
*
* Camunda licenses this file to you under the MIT; you may not use this file
* except in compliance with the MIT License.
*/

import { useEffect, useState } from 'react';

export default function useAsyncMemo(fn, deps, initialValue) {
const [ value, setValue ] = useState(initialValue);

useEffect(() => {
fn().then(setValue);
}, deps);

return value;
}
1 change: 1 addition & 0 deletions client/src/app/tabs/robot/RobotEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export class RobotEditor extends CachedComponent {
name={ this.props.file?.name }
id={ this.props.id }
onAction={ this.props.onAction }
{ ...this.props }
/>
</div>
);
Expand Down

0 comments on commit 22499d8

Please sign in to comment.