diff --git a/spiffworkflow-backend/migrations/env.py b/spiffworkflow-backend/migrations/env.py index 630e381a..68feded2 100644 --- a/spiffworkflow-backend/migrations/env.py +++ b/spiffworkflow-backend/migrations/env.py @@ -1,3 +1,5 @@ +from __future__ import with_statement + import logging from logging.config import fileConfig diff --git a/spiffworkflow-backend/migrations/versions/7c12964efde1_.py b/spiffworkflow-backend/migrations/versions/50dd2e016d94_.py similarity index 99% rename from spiffworkflow-backend/migrations/versions/7c12964efde1_.py rename to spiffworkflow-backend/migrations/versions/50dd2e016d94_.py index 9a285b72..a702c5a4 100644 --- a/spiffworkflow-backend/migrations/versions/7c12964efde1_.py +++ b/spiffworkflow-backend/migrations/versions/50dd2e016d94_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 7c12964efde1 +Revision ID: 50dd2e016d94 Revises: -Create Date: 2022-11-08 07:48:44.265652 +Create Date: 2022-11-08 16:28:18.991635 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '7c12964efde1' +revision = '50dd2e016d94' down_revision = None branch_labels = None depends_on = None @@ -95,7 +95,7 @@ def upgrade(): ) op.create_table('process_instance', sa.Column('id', sa.Integer(), nullable=False), - sa.Column('process_model_identifier', sa.String(length=50), nullable=False), + sa.Column('process_model_identifier', sa.String(length=255), nullable=False), sa.Column('process_group_identifier', sa.String(length=50), nullable=False), sa.Column('process_initiator_id', sa.Integer(), nullable=False), sa.Column('bpmn_json', sa.JSON(), nullable=True), diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index 50c3c9f7..c06126f2 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -72,7 +72,7 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel): __tablename__ = "process_instance" id: int = db.Column(db.Integer, primary_key=True) - process_model_identifier: str = db.Column(db.String(50), nullable=False, index=True) + process_model_identifier: str = db.Column(db.String(255), nullable=False, index=True) process_group_identifier: str = db.Column(db.String(50), nullable=False, index=True) process_initiator_id: int = db.Column(ForeignKey(UserModel.id), nullable=False) process_initiator = relationship("UserModel") diff --git a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js index de2692b2..60ec98fa 100644 --- a/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/process_instances.cy.js @@ -68,6 +68,7 @@ describe('process-instances', () => { cy.login(); cy.navigateToProcessModel( 'Acceptance Tests Group One', + 'Acceptance Tests Model 1', 'acceptance-tests-model-1' ); }); @@ -75,7 +76,7 @@ describe('process-instances', () => { cy.logout(); }); - it.only('can create a new instance and can modify', () => { + it('can create a new instance and can modify', () => { const originalDmnOutputForKevin = 'Very wonderful'; const newDmnOutputForKevin = 'The new wonderful'; const dmnOutputForDan = 'pretty wonderful'; @@ -90,28 +91,29 @@ describe('process-instances', () => { cy.runPrimaryBpmnFile(); // Change dmn - cy.contains(dmnFile).click(); - cy.contains(`Process Model File: ${dmnFile}`); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${dmnFile.replace('.', '-')}`).click(); updateDmnText(originalDmnOutputForKevin, newDmnOutputForKevin); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); - cy.contains(dmnFile).click(); - cy.contains(`Process Model File: ${dmnFile}`); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${dmnFile.replace('.', '-')}`).click(); updateDmnText(newDmnOutputForKevin, originalDmnOutputForKevin); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); // Change bpmn - cy.contains(bpmnFile).click(); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); cy.contains(`Process Model File: ${bpmnFile}`); updateBpmnPythonScript(newPythonScript); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); - cy.contains(bpmnFile).click(); - cy.contains(`Process Model File: ${bpmnFile}`); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); updateBpmnPythonScript(originalPythonScript); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); @@ -125,13 +127,15 @@ describe('process-instances', () => { const bpmnFile = 'process_model_one.bpmn'; // Change bpmn - cy.contains(bpmnFile).click(); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); cy.contains(`Process Model File: ${bpmnFile}`); updateBpmnPythonScriptWithMonaco(newPythonScript); cy.contains('acceptance-tests-model-1').click(); cy.runPrimaryBpmnFile(); - cy.contains(bpmnFile).click(); + cy.getBySel('files-accordion').click(); + cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); cy.contains(`Process Model File: ${bpmnFile}`); updateBpmnPythonScriptWithMonaco(originalPythonScript); cy.contains('acceptance-tests-model-1').click(); diff --git a/spiffworkflow-frontend/cypress/e2e/tasks.cy.js b/spiffworkflow-frontend/cypress/e2e/tasks.cy.js index 377b05ac..9d5b836a 100644 --- a/spiffworkflow-frontend/cypress/e2e/tasks.cy.js +++ b/spiffworkflow-frontend/cypress/e2e/tasks.cy.js @@ -25,10 +25,11 @@ describe('tasks', () => { it('can complete and navigate a form', () => { const groupDisplayName = 'Acceptance Tests Group One'; const modelId = `acceptance-tests-model-2`; + const modelDisplayName = `Acceptance Tests Model 2`; const completedTaskClassName = 'completed-task-highlight'; const activeTaskClassName = 'active-task-highlight'; - cy.navigateToProcessModel(groupDisplayName, modelId); + cy.navigateToProcessModel(groupDisplayName, modelDisplayName, modelId); // avoid reloading so we can click on the task link that appears on running the process instance cy.runPrimaryBpmnFile(false); @@ -67,7 +68,7 @@ describe('tasks', () => { ); cy.contains('Task: get_user_generated_number_four'); - cy.navigateToProcessModel(groupDisplayName, modelId); + cy.navigateToProcessModel(groupDisplayName, modelDisplayName, modelId); cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); @@ -84,7 +85,7 @@ describe('tasks', () => { checkTaskHasClass('form2', completedTaskClassName); checkTaskHasClass('form3', completedTaskClassName); checkTaskHasClass('form4', activeTaskClassName); - cy.get('.modal .btn-close').click(); + cy.get('.is-visible .cds--modal-close').click(); cy.navigateToHome(); cy.contains('Tasks').should('exist'); @@ -99,7 +100,7 @@ describe('tasks', () => { ); cy.url().should('include', '/tasks'); - cy.navigateToProcessModel(groupDisplayName, modelId); + cy.navigateToProcessModel(groupDisplayName, modelDisplayName, modelId); cy.getBySel('process-instance-list-link').click(); cy.assertAtLeastOneItemInPaginatedResults(); @@ -112,6 +113,7 @@ describe('tasks', () => { it('can paginate items', () => { cy.navigateToProcessModel( 'Acceptance Tests Group One', + 'Acceptance Tests Model 2', 'acceptance-tests-model-2' ); diff --git a/spiffworkflow-frontend/cypress/support/commands.js b/spiffworkflow-frontend/cypress/support/commands.js index affce0d1..c9619161 100644 --- a/spiffworkflow-frontend/cypress/support/commands.js +++ b/spiffworkflow-frontend/cypress/support/commands.js @@ -91,13 +91,12 @@ Cypress.Commands.add('runPrimaryBpmnFile', (reload = true) => { Cypress.Commands.add( 'navigateToProcessModel', - (groupDisplayName, modelDisplayName) => { + (groupDisplayName, modelDisplayName, modelIdentifier) => { cy.navigateToAdmin(); cy.contains(groupDisplayName).click(); cy.contains(`Process Group: ${groupDisplayName}`); // https://stackoverflow.com/q/51254946/6090676 - cy.getBySel('process-model-show-link').contains(modelDisplayName).click(); - // cy.url().should('include', `process-models/${groupDisplayName}/${modelDisplayName}`); + cy.getBySel('process-model-show-link').contains(modelIdentifier).click(); cy.contains(`Process Model: ${modelDisplayName}`); } ); @@ -115,10 +114,7 @@ Cypress.Commands.add('basicPaginationTest', () => { }); Cypress.Commands.add('assertAtLeastOneItemInPaginatedResults', () => { - cy.getBySel('total-paginated-items') - .invoke('text') - .then(parseFloat) - .should('be.gt', 0); + cy.contains(/\b[1-9]\d*–[1-9]\d* of [1-9]\d*/); }); Cypress.Commands.add('assertNoItemInPaginatedResults', () => { diff --git a/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx b/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx index 36b42de4..dcdd7bd8 100644 --- a/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx +++ b/spiffworkflow-frontend/src/components/ProcessBreadcrumb.tsx @@ -55,7 +55,10 @@ export default function ProcessBreadcrumb({ const leadingCrumbLinks = hotCrumbs.map((crumb: any) => { const valueLabel = crumb[0]; const url = crumb[1]; - if (url.startsWith('process_model:')) { + if (!url) { + return {valueLabel}; + } + if (url && url.startsWith('process_model:')) { return explodeCrumb(crumb); } return ( diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css index c9ddd249..b1eedf70 100644 --- a/spiffworkflow-frontend/src/index.css +++ b/spiffworkflow-frontend/src/index.css @@ -44,6 +44,14 @@ span.bjs-crumb { vertical-align: middle; } +.cds--breadcrumb { + margin-bottom: 2em; +} + +.process-description { + margin-bottom: 2em; +} + .diagram-editor-canvas { border:1px solid #000000; height:70vh; diff --git a/spiffworkflow-frontend/src/index.scss b/spiffworkflow-frontend/src/index.scss index 2adade11..37766c21 100644 --- a/spiffworkflow-frontend/src/index.scss +++ b/spiffworkflow-frontend/src/index.scss @@ -25,6 +25,21 @@ // background-color: colors.$gray-100; color: white; } +h1{ + height: 36px; + font-family: 'IBM Plex Sans'; + font-style: normal; + font-weight: 400; + font-size: 28px; + line-height: 36px; + color: #161616; + flex: none; + order: 0; + align-self: stretch; + flex-grow: 0; + margin-bottom: 1em +} + .cds--breadcrumb-item a.cds--link:hover { color: #525252; diff --git a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx index 03968b49..85dfc482 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelEditDiagram.tsx @@ -77,7 +77,9 @@ export default function ProcessModelEditDiagram() { const [searchParams] = useSearchParams(); const setErrorMessage = (useContext as any)(ErrorContext)[1]; - const [processModelFile, setProcessModelFile] = useState(null); + const [processModelFile, setProcessModelFile] = useState( + null + ); const [newFileName, setNewFileName] = useState(''); const [bpmnXmlForDiagramRendering, setBpmnXmlForDiagramRendering] = useState(null); @@ -774,16 +776,24 @@ export default function ProcessModelEditDiagram() { // if a file name is not given then this is a new model and the ReactDiagramEditor component will handle it if ((bpmnXmlForDiagramRendering || !params.file_name) && processModel) { + const processModelFileName = processModelFile + ? `: ${processModelFile.name}` + : ''; return ( <> Process Model File - {processModelFile ? `: ${(processModelFile as any).name}` : ''} + {processModelFileName} {appropriateEditor()} {newFileNameBox()} diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx index f5a68f71..42e4207b 100644 --- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx @@ -274,6 +274,7 @@ export default function ProcessModelShow() { iconDescription="Edit File" hasIconOnly size="lg" + data-qa={`edit-file-${processModelFile.name.replace('.', '-')}`} onClick={() => navigateToFileEdit(processModelFile)} /> ); @@ -455,6 +456,7 @@ export default function ProcessModelShow() { return ( @@ -524,8 +526,8 @@ export default function ProcessModelShow() { ], ]} /> - {processModel.display_name} - {processModel.description} + Process Model: {processModel.display_name} + {processModel.description} Run
{processModel.description}