16068: Merge branch 'main' of git.arvados.org:arvados-workbench2 into 16068
authorStephen Smith <stephen@curii.com>
Tue, 12 Apr 2022 21:16:15 +0000 (17:16 -0400)
committerStephen Smith <stephen@curii.com>
Tue, 12 Apr 2022 21:16:15 +0000 (17:16 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

1  2 
cypress/integration/collection.spec.js
cypress/integration/process.spec.js
cypress/support/commands.js
src/views/process-panel/process-details-card.tsx

index f5dfbcd4bf4827b5c55674429e8175de7288c347,3234f7c4bd5bcd04a89df3274642b467a2e3a2f6..48b936cf69c8e50e225b32e8d316826cec67c141
@@@ -191,4 -191,58 +191,58 @@@ describe('Process tests', function() 
              });
          });
      });
 -});
+     it('should show runtime status indicators', function() {
+         // Setup running container with runtime_status error & warning messages
+         createContainerRequest(
+             activeUser,
+             'test_container_request',
+             'arvados/jobs',
+             ['echo', 'hello world'],
+             false, 'Committed')
+         .as('containerRequest')
+         .then(function(containerRequest) {
+             expect(containerRequest.state).to.equal('Committed');
+             expect(containerRequest.container_uuid).not.to.be.equal('');
+             cy.getContainer(activeUser.token, containerRequest.container_uuid)
+             .then(function(queuedContainer) {
+                 expect(queuedContainer.state).to.be.equal('Queued');
+             });
+             cy.updateContainer(adminUser.token, containerRequest.container_uuid, {
+                 state: 'Locked'
+             }).then(function(lockedContainer) {
+                 expect(lockedContainer.state).to.be.equal('Locked');
+                 cy.updateContainer(adminUser.token, lockedContainer.uuid, {
+                     state: 'Running',
+                     runtime_status: {
+                         error: 'Something went wrong',
+                         errorDetail: 'Process exited with status 1',
+                         warning: 'Free disk space is low',
+                     }
+                 })
+                 .as('runningContainer')
+                 .then(function(runningContainer) {
+                     expect(runningContainer.state).to.be.equal('Running');
+                     expect(runningContainer.runtime_status).to.be.deep.equal({
+                         'error': 'Something went wrong',
+                         'errorDetail': 'Process exited with status 1',
+                         'warning': 'Free disk space is low',
+                     });
+                 });
+             })
+         });
+         // Test that the UI shows the error and warning messages
+         cy.getAll('@containerRequest', '@runningContainer').then(function([containerRequest]) {
+             cy.loginAs(activeUser);
+             cy.goToPath(`/processes/${containerRequest.uuid}`);
+             cy.get('[data-cy=process-runtime-status-error]')
+                 .should('contain', 'Something went wrong')
+                 .and('contain', 'Process exited with status 1');
+             cy.get('[data-cy=process-runtime-status-warning]')
+                 .should('contain', 'Free disk space is low')
+                 .and('contain', 'No additional warning details available');
+         });
+     });
 +});
Simple merge
index 5cca904a0d53382672f1ef5183e27a182aff6069,d3349c3ae8d79ea62d7dcb052ab6852bd8d43f4b..59d0b61b20f18e2a2a1b8ee6cd581819668f98be
@@@ -12,17 -12,14 +12,17 @@@ import 
      IconButton,
      CardContent,
      Tooltip,
-     Chip,
 +    Typography,
  } from '@material-ui/core';
  import { ArvadosTheme } from 'common/custom-theme';
 -import { CloseIcon } from 'components/icon/icon';
 +import { CloseIcon, MoreOptionsIcon, ProcessIcon } from 'components/icon/icon';
- import { Process, getProcessStatus, getProcessStatusColor } from 'store/processes/process';
+ import { Process } from 'store/processes/process';
  import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
  import { ProcessDetailsAttributes } from './process-details-attributes';
++import { ProcessStatus } from 'views-components/data-explorer/renderers';
 +import { ContainerState } from 'models/container';
  
- type CssRules = 'card' | 'content' | 'title' | 'header' | 'cancelButton' | 'chip' | 'avatar' | 'iconHeader';
 -type CssRules = 'card' | 'content' | 'title' | 'header';
++type CssRules = 'card' | 'content' | 'title' | 'header' | 'cancelButton' | 'avatar' | 'iconHeader';
  
  const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
      card: {
          overflow: 'hidden',
          paddingTop: theme.spacing.unit * 0.5
      },
-     chip: {
-         height: theme.spacing.unit * 3,
-         width: theme.spacing.unit * 12,
-         color: theme.palette.common.white,
-         fontSize: '0.875rem',
-         borderRadius: theme.spacing.unit * 0.625,
-     },
 +    cancelButton: {
 +        paddingRight: theme.spacing.unit * 2,
 +        fontSize: '14px',
 +        color: theme.customs.colors.red900,
 +        "&:hover": {
 +            cursor: 'pointer'
 +        }
 +    },
  });
  
  export interface ProcessDetailsCardDataProps {
      process: Process;
 +    cancelProcess: (uuid: string) => void;
 +    onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
  }
  
- type ProcessDetailsCardProps = ProcessDetailsCardDataProps & WithStyles<CssRules, true> & MPVPanelProps;
+ type ProcessDetailsCardProps = ProcessDetailsCardDataProps & WithStyles<CssRules> & MPVPanelProps;
  
- export const ProcessDetailsCard = withStyles(styles, {withTheme: true})(
-     ({ theme, cancelProcess, onContextMenu, classes, process, doHidePanel, panelName }: ProcessDetailsCardProps) => {
+ export const ProcessDetailsCard = withStyles(styles)(
 -    ({ classes, process, doHidePanel, panelName }: ProcessDetailsCardProps) => {
++    ({ cancelProcess, onContextMenu, classes, process, doHidePanel, panelName }: ProcessDetailsCardProps) => {
          return <Card className={classes.card}>
              <CardHeader
                  className={classes.header}
                  classes={{
                      content: classes.title,
 +                    avatar: classes.avatar,
                  }}
 -                title='Details'
 -                action={ doHidePanel &&
 +                avatar={<ProcessIcon className={classes.iconHeader} />}
 +                title={
 +                    <Tooltip title={process.containerRequest.name} placement="bottom-start">
 +                        <Typography noWrap variant='h6' color='inherit'>
 +                            {process.containerRequest.name}
 +                        </Typography>
 +                    </Tooltip>
 +                }
 +                subheader={
 +                    <Tooltip title={getDescription(process)} placement="bottom-start">
 +                        <Typography noWrap variant='body1' color='inherit'>
 +                            {getDescription(process)}
 +                        </Typography>
 +                    </Tooltip>}
 +                action={
 +                    <div>
 +                        {process.container && process.container.state === ContainerState.RUNNING &&
 +                            <span className={classes.cancelButton} onClick={() => cancelProcess(process.containerRequest.uuid)}>Cancel</span>}
-                         <Chip label={getProcessStatus(process)}
-                             className={classes.chip}
-                             style={{ backgroundColor: getProcessStatusColor(getProcessStatus(process), theme as ArvadosTheme) }} />
++                        <ProcessStatus uuid={process.containerRequest.uuid} />
 +                        <Tooltip title="More options" disableFocusListener>
 +                            <IconButton
 +                                aria-label="More options"
 +                                onClick={event => onContextMenu(event)}>
 +                                <MoreOptionsIcon />
 +                            </IconButton>
 +                        </Tooltip>
 +                        { doHidePanel &&
                          <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
                              <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
 -                        </Tooltip> } />
 +                        </Tooltip> }
 +                    </div>
 +                } />
              <CardContent className={classes.content}>
 -                <ProcessDetailsAttributes item={process.containerRequest} twoCol />
 +                <ProcessDetailsAttributes request={process.containerRequest} twoCol />
              </CardContent>
          </Card>;
      }