Skip to content

Commit

Permalink
added reset button
Browse files Browse the repository at this point in the history
  • Loading branch information
luth1um committed Sep 3, 2024
1 parent a083f75 commit 13e3fcb
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 35 deletions.
30 changes: 30 additions & 0 deletions e2e/reset.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { TaFixture } from '../test/fixture/timedAutomatonFixture';
import { TEST_BASE_URL } from './helper/endToEndTestConstants';
import { test } from './helper/testOptions';
import { expect } from '@playwright/test';

test.describe('For resetting the TA', () => {
test.beforeEach(async ({ page }) => {
await page.goto(TEST_BASE_URL);
});

test('the initial page should contain an enabled button for resetting', async ({ page }) => {
await expect(page.getByTestId('button-open-reset')).toBeVisible();
await expect(page.getByTestId('button-open-reset')).toBeEnabled();
});

test('the reset button should reset the TA to its original state', async ({ page, taUiHelper }) => {
// given
const initialTa = await taUiHelper.readTaFromUi();
const otherTa = TaFixture.withTwoLocationsAndTwoSwitches();
await taUiHelper.setTimedAutomatonTo(otherTa);

// when
await page.getByTestId('button-open-reset').click();
await page.getByTestId('button-confirm-ta-reset').click();

// then
const taAfterReset = await taUiHelper.readTaFromUi();
expect(taAfterReset, 'TA after reset should be the same as the initial TA').toEqual(initialTa);
});
});
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ export default tseslint.config(
},
{
// needs to be in its own object to act as global ignore
ignores: ['dist', '*.cjs'],
ignores: ['dist', 'playwright-report', '*.cjs'],
}
);
5 changes: 5 additions & 0 deletions public/locales/de/translation.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"app.title": "Analyse von Timed Automata",
"manipulation.button.reachability": "Erreichbarkeit",
"manipulation.button.reset": "TA zurücksetzen",
"manipulation.table.showContent": "{{content}} ausklappen",
"manipulation.table.hideContent": "{{content}} einklappen",
"manipulation.table.addElement": "{{content}} hinzufügen",
Expand All @@ -21,6 +22,10 @@
"analysisDialog.analysis.resultSomeUnreachable": "Die folgenden Orte sind unerreichbar:",
"analysisDialog.button.close": "Schließen",
"analysisDialog.button.analyze": "Starten",
"resetDialog.title": "Zurücksetzen bestätigen",
"resetDialog.contentText": "Bist du sicher, dass du den TA zurücksetzen möchtest? Alle Änderungen gehen dabei verloren.",
"resetDialog.button.cancel": "Abbrechen",
"resetDialog.button.reset": "Zurücksetzen",
"locDialog.errorNameEmpty": "Name darf nicht leer sein",
"locDialog.errorNameExists": "Name wird bereits verwendet",
"locDialog.editLoc": "Ort bearbeiten",
Expand Down
5 changes: 5 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"app.title": "Timed-Automata Analysis",
"manipulation.button.reachability": "Reachability",
"manipulation.button.reset": "Reset TA",
"manipulation.table.showContent": "Show {{content}}",
"manipulation.table.hideContent": "Hide {{content}}",
"manipulation.table.addElement": "Add {{content}}",
Expand All @@ -21,6 +22,10 @@
"analysisDialog.analysis.resultSomeUnreachable": "The following locations are unreachable:",
"analysisDialog.button.close": "Close",
"analysisDialog.button.analyze": "Analyze",
"resetDialog.title": "Confirm Reset",
"resetDialog.contentText": "Are you sure that you want to reset the TA? All changes will be lost.",
"resetDialog.button.cancel": "Cancel",
"resetDialog.button.reset": "Reset",
"locDialog.errorNameEmpty": "Name cannot be empty",
"locDialog.errorNameExists": "Name already exists",
"locDialog.editLoc": "Edit Location",
Expand Down
6 changes: 5 additions & 1 deletion src/utils/initAutomaton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ const SWITCH_0: Switch = {
target: LOC_1,
};

export const INIT_AUTOMATON: TimedAutomaton = {
const INIT_AUTOMATON: TimedAutomaton = {
locations: [LOC_0, LOC_1],
clocks: [CLOCK_0, CLOCK_1],
switches: [SWITCH_0],
};

export function getInitAutomaton(): TimedAutomaton {
return structuredClone(INIT_AUTOMATON);
}
48 changes: 35 additions & 13 deletions src/view/AutomatonManipulation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ClockDeleteConfirmDialog from './ClockDeleteConfirmDialog';
import ManipulateClockDialog from './ManipulateClockDialog';
import { AnalysisDialog } from './AnalysisDialog';
import { useButtonUtils } from '../utils/buttonUtils';
import { ResetDialog } from './ResetDialog';

interface ManipulationProps {
viewModel: AnalysisViewModel;
Expand All @@ -35,6 +36,7 @@ export const AutomatonManipulation: React.FC<ManipulationProps> = (props) => {
addClock,
editClock,
removeClock,
setStateReset,
} = viewModel;
const { locations, switches, clocks } = ta;
const { t } = useTranslation();
Expand Down Expand Up @@ -275,25 +277,45 @@ export const AutomatonManipulation: React.FC<ManipulationProps> = (props) => {
const handleAnalysisOpen = () => setAnalysisOpen(true);
const handleAnalysisClose = () => setAnalysisOpen(false);

// ===== handle reset ========================================================

const [resetWarnOpen, setResetWarnOpen] = useState(false);
const handleResetOpen = () => setResetWarnOpen(true);
const handleResetClose = () => setResetWarnOpen(false);
const handleReset = () => setStateReset(viewModel);

// ===========================================================================

return (
<>
<div key={'analysis-button-div'} style={{ marginBottom: '16px' }}>
<Button
onMouseDown={handleAnalysisOpen}
onKeyDown={(e) => executeOnKeyboardClick(e.key, handleAnalysisClose)}
variant="contained"
color="primary"
size="small"
data-testid={'button-open-analysis'}
disabled={state !== AnalysisState.READY}
>
{t('manipulation.button.reachability')}
</Button>
</div>
<Button
style={{ marginBottom: '4px' }}
onMouseDown={handleAnalysisOpen}
onKeyDown={(e) => executeOnKeyboardClick(e.key, handleAnalysisOpen)}
variant="contained"
color="primary"
size="small"
data-testid={'button-open-analysis'}
disabled={state !== AnalysisState.READY}
>
{t('manipulation.button.reachability')}
</Button>
<br />
<Button
style={{ marginBottom: '16px' }}
onMouseDown={handleResetOpen}
onKeyDown={(e) => executeOnKeyboardClick(e.key, handleResetOpen)}
variant="contained"
color="error"
size="small"
data-testid={'button-open-reset'}
disabled={state !== AnalysisState.READY}
>
{t('manipulation.button.reset')}
</Button>
{allTables}
<AnalysisDialog open={analysisOpen} viewModel={viewModel} handleClose={handleAnalysisClose} />
<ResetDialog open={resetWarnOpen} handleClose={handleResetClose} handleReset={handleReset} />
<ManipulateLocationDialog
open={locationAddOpen}
locations={locations}
Expand Down
22 changes: 20 additions & 2 deletions src/view/ClockDeleteConfirmDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import React from 'react';
import { Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Button } from '@mui/material';
import {
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Button,
IconButton,
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { Clock } from '../model/ta/clock';
import { useTranslation } from 'react-i18next';
import { useButtonUtils } from '../utils/buttonUtils';
Expand All @@ -26,7 +35,16 @@ const ClockDeleteConfirmDialog: React.FC<ClockDeleteConfirmDialogProps> = (props

return (
<Dialog open={open} onClose={onClose} data-testid="dialog-delete-clock-confirm">
<DialogTitle>{t('deleteClockConfirmDialog.title', { clockName: clock.name })}</DialogTitle>
<DialogTitle>
{t('deleteClockConfirmDialog.title', { clockName: clock.name })}
<IconButton
onMouseDown={onClose}
onKeyDown={(e) => executeOnKeyboardClick(e.key, onClose)}
sx={{ position: 'absolute', right: 8, top: 8, color: (theme) => theme.palette.grey[500] }}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<DialogContentText>{t('deleteClockConfirmDialog.contentText', { clockName: clock.name })}</DialogContentText>
<p>
Expand Down
66 changes: 66 additions & 0 deletions src/view/ResetDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useTranslation } from 'react-i18next';
import { useButtonUtils } from '../utils/buttonUtils';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
IconButton,
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';

interface ResetDialogProps {
open: boolean;
handleClose: () => void;
handleReset: () => void;
}

export const ResetDialog: React.FC<ResetDialogProps> = (props) => {
const { open, handleClose, handleReset } = props;
const { t } = useTranslation();
const { executeOnKeyboardClick } = useButtonUtils();

const confirmReset = () => {
handleClose();
handleReset();
};

return (
<Dialog open={open} onClose={handleClose}>
<DialogTitle>
{t('resetDialog.title')}
<IconButton
onMouseDown={handleClose}
onKeyDown={(e) => executeOnKeyboardClick(e.key, handleClose)}
sx={{ position: 'absolute', right: 8, top: 8, color: (theme) => theme.palette.grey[500] }}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<DialogContentText>{t('resetDialog.contentText')}</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onMouseDown={handleClose}
onKeyDown={(e) => executeOnKeyboardClick(e.key, handleClose)}
variant="contained"
color="primary"
>
{t('resetDialog.button.cancel')}
</Button>
<Button
onMouseDown={confirmReset}
onKeyDown={(e) => executeOnKeyboardClick(e.key, confirmReset)}
variant="contained"
color="error"
data-testid="button-confirm-ta-reset"
>
{t('resetDialog.button.reset')}
</Button>
</DialogActions>
</Dialog>
);
};
41 changes: 23 additions & 18 deletions src/viewmodel/AnalysisViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useMathUtils } from '../utils/mathUtils';
import { useSwitchUtils } from '../utils/switchUtils';
import { useClockConstraintUtils } from '../utils/clockConstraintUtils';
import { useClockUtils } from '../utils/clockUtils';
import { INIT_AUTOMATON } from '../utils/initAutomaton';
import { getInitAutomaton } from '../utils/initAutomaton';

export interface AnalysisViewModel {
state: AnalysisState;
Expand Down Expand Up @@ -57,6 +57,7 @@ export interface AnalysisViewModel {
removeClock: (viewModel: AnalysisViewModel, clock: Clock) => void;
setStateAnalyzing: (viewModel: AnalysisViewModel) => void;
setStateReady: (viewModel: AnalysisViewModel) => void;
setStateReset: (viewModel: AnalysisViewModel) => void;
}

export enum AnalysisState {
Expand Down Expand Up @@ -287,24 +288,29 @@ export function useAnalysisViewModel(): AnalysisViewModel {
setViewModel({ ...viewModel, state: AnalysisState.READY });
}, []);

const setStateReset = useCallback((viewModel: AnalysisViewModel) => {
setViewModel({ ...viewModel, state: AnalysisState.RESET });
}, []);

// ===== manipulate state ====================================================

const [viewModel, setViewModel] = useState<AnalysisViewModel>({
state: AnalysisState.INIT,
ta: INIT_AUTOMATON,
addLocation: addLocation,
editLocation: editLocation,
removeLocation: removeLocation,
setInitialLocation: setInitialLocation,
updateLocationCoordinates: updateLocationCoordinates,
addSwitch: addSwitch,
editSwitch: editSwitch,
removeSwitch: removeSwitch,
addClock: addClock,
editClock: editClock,
removeClock: removeClock,
setStateAnalyzing: setStateAnalyzing,
setStateReady: setStateReady,
ta: getInitAutomaton(),
addLocation,
editLocation,
removeLocation,
setInitialLocation,
updateLocationCoordinates,
addSwitch,
editSwitch,
removeSwitch,
addClock,
editClock,
removeClock,
setStateAnalyzing,
setStateReady,
setStateReset,
});

// ===================================================================================================================
Expand All @@ -318,14 +324,13 @@ export function useAnalysisViewModel(): AnalysisViewModel {

useEffect(() => {
if (viewModel.state === AnalysisState.ANALYZING) {
setViewModel({ ...viewModel, state: AnalysisState.READY });
// nothing to do here at the moment
}
}, [viewModel]);

useEffect(() => {
if (viewModel.state === AnalysisState.RESET) {
// TODO: add a reset button to use this reset
setViewModel({ ...viewModel, ta: INIT_AUTOMATON, state: AnalysisState.READY });
setViewModel({ ...viewModel, ta: getInitAutomaton(), state: AnalysisState.READY });
}
}, [viewModel]);

Expand Down

0 comments on commit 13e3fcb

Please sign in to comment.