Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .cursor/rules/cypress-testing-takeaways.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
description: Cypress component test takeaways for this repo
alwaysApply: true
---

# Cypress Component Testing Takeaways

- Avoid stubbing ESM imports in Cypress CT; prefer data-driven setup via providers.
- Build the full provider stack: `GlobalProvider`, Redux store, `OrbitContext`,
and any feature contexts (e.g. `PassageDetailContext`, `UnsavedContext`).
- For permission logic (`useStepPermissions`), drive outcomes with real mock records and relationships.
- Localization selectors need `LocalizedStrings` in the correct `strings` slice keys.
- Use stable DOM selectors (ids/data-cy) for assertions.
- When testing navigation that uses `usePassageNavigate`, include a `MemoryRouter`
- `Routes` and stub `UnsavedContext.checkSavedFn` to run immediately.
- `nextPasId`/`prevPasId` depend on `section.relationships.passages` and
`memory` records; empty relationships mean no neighbors.
- `WorkflowStepsMobile` renders SVG stages (no visible text nodes). Assert on
structure (`svg`, `svg g`) or wrapper labels, not `cy.contains` on step names.
- `WorkflowStepsMobile` depends on `workflow`/`currentstep` from context; keep
defaults intact when overriding to avoid empty steps.
- For `WorkflowStepsMobile`, set viewport and dispatch a `resize` after mount so
the width-driven step list is computed.
- When testing busy/recording state, assert via `setCurrentStep` calls and snack
messages rather than DOM class changes.
- Pagination needs enough steps and a small viewport to expose `prev`/`next`.
- Run the component spec and iterate on failures:
- `cd src\renderer`
- `npm run cy:run-ct -- --spec=**/YourComponent.cy.tsx`

## Example: Minimal Strings Slice

```ts
const mockStringsReducer = () => ({
loaded: true,
lang: 'en',
passageDetailStepComplete: new LocalizedStrings({
en: { title: 'Complete' },
}),
});
```
21 changes: 21 additions & 0 deletions .cursor/rules/jest-testing-takeaways.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
description: Jest testing takeaways for this repo
alwaysApply: true
---

# Jest Testing Takeaways

- Favor unit tests for non-React modules (pure functions, CRUD helpers).
- Mock Orbit helpers (`related`, `findRecord`) and other dependencies; keep test data minimal.
- Cover edge cases: missing relationships, missing current id, filtering, wrap-around.
- Prefer deterministic sorting in tests (explicit `sequencenum`).
- Use `--runInBand --watchAll=false` to avoid hanging runs in CI/automation.

## Running a New Jest Test Module

Run Jest from `src/renderer` (required for this repo).

```bash
cd src/renderer
npm test -- <moduleName> --runInBand --watchAll=false
```
92 changes: 92 additions & 0 deletions localization/TranscriberAdmin-en-1.2.xliff
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,13 @@
<context context-type="sourcefile">SpeakerName.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="community.autoSegment">
<source>Use {0} for autosegmentation, or tap in the waveform and use + for manual segmentation</source>
<target/>
<context-group>
<context context-type="sourcefile">SpeakerName.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="community.backTranslationComplete">
<source>Version {0}: Back translation: {1}</source>
<target/>
Expand Down Expand Up @@ -1564,6 +1571,13 @@
<context context-type="sourcefile">SpeakerName.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="community.listen">
<source>Use {0} to listen to the passage</source>
<target/>
<context-group>
<context context-type="sourcefile">SpeakerName.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="community.loading">
<source>Loading:</source>
<target/>
Expand Down Expand Up @@ -5211,6 +5225,56 @@
<context context-type="sourcefile">MediaUpload.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.addSegment">
<source>Add segment boundary</source>
<target/>
<context-group>
<context context-type="sourcefile">SegmentControlIsMobile.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.next">
<source>Next</source>
<target/>
<context-group>
<context context-type="sourcefile">PassageDetailMobileFooter.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.previous">
<source>Previous</source>
<target/>
<context-group>
<context context-type="sourcefile">PassageDetailMobileFooter.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.removeSegment">
<source>Remove next segment boundary</source>
<target/>
<context-group>
<context context-type="sourcefile">SegmentControlIsMobile.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.resetBT">
<source>Reset back translation</source>
<target/>
<context-group>
<context context-type="sourcefile">SegmentControlIsMobile.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.restoreBoundaries">
<source>Restore original segment boundaries</source>
<target/>
<context-group>
<context context-type="sourcefile">SegmentControlIsMobile.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="mobile.segment">
<source>Segment: {0}</source>
<target/>
<context-group>
<context context-type="sourcefile">SegmentControlIsMobile.tsx</context>
</context-group>
</trans-unit>

<trans-unit id="newProject.audioProduct">
<source>Audio Product</source>
<target/>
Expand Down Expand Up @@ -7480,6 +7544,27 @@
<context context-type="sourcefile">ResourceOverview.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="recordButton.record">
<source>RECORD</source>
<target/>
<context-group>
<context context-type="sourcefile">RecordButton.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="recordButton.rerecord">
<source>RERECORD</source>
<target/>
<context-group>
<context context-type="sourcefile">RecordButton.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="recordButton.resume">
<source>RESUME</source>
<target/>
<context-group>
<context context-type="sourcefile">RecordButton.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="recordStepSettings.custom">
<source>Custom</source>
<target/>
Expand Down Expand Up @@ -11010,6 +11095,13 @@
<context context-type="sourcefile">wsAudioPlayer.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="wsAudioPlayer.reset">
<source>Reset</source>
<target/>
<context-group>
<context context-type="sourcefile">wsAudioPlayer.tsx</context>
</context-group>
</trans-unit>
<trans-unit id="wsAudioPlayer.resume">
<source>Resume</source>
<target/>
Expand Down
78 changes: 78 additions & 0 deletions localization/TranscriberAdmin-en.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,12 @@
<target/>
</segment>
</unit>
<unit id="community.autoSegment">
<segment>
<source>Use {0} for autosegmentation, or tap in the waveform and use + for manual segmentation</source>
<target/>
</segment>
</unit>
<unit id="community.backTranslationComplete">
<segment>
<source>Version {0}: Back translation: {1}</source>
Expand All @@ -1342,6 +1348,12 @@
<target/>
</segment>
</unit>
<unit id="community.listen">
<segment>
<source>Use {0} to listen to the passage</source>
<target/>
</segment>
</unit>
<unit id="community.loading">
<segment>
<source>Loading:</source>
Expand Down Expand Up @@ -4468,6 +4480,48 @@
<target/>
</segment>
</unit>
<unit id="mobile.addSegment">
<segment>
<source>Add segment boundary</source>
<target/>
</segment>
</unit>
<unit id="mobile.next">
<segment>
<source>Next</source>
<target/>
</segment>
</unit>
<unit id="mobile.previous">
<segment>
<source>Previous</source>
<target/>
</segment>
</unit>
<unit id="mobile.removeSegment">
<segment>
<source>Remove next segment boundary</source>
<target/>
</segment>
</unit>
<unit id="mobile.resetBT">
<segment>
<source>Reset back translation</source>
<target/>
</segment>
</unit>
<unit id="mobile.restoreBoundaries">
<segment>
<source>Restore original segment boundaries</source>
<target/>
</segment>
</unit>
<unit id="mobile.segment">
<segment>
<source>Segment: {0}</source>
<target/>
</segment>
</unit>
<unit id="newProject.audioProduct">
<segment>
<source>Audio Product</source>
Expand Down Expand Up @@ -6412,6 +6466,24 @@
<target/>
</segment>
</unit>
<unit id="recordButton.record">
<segment>
<source>RECORD</source>
<target/>
</segment>
</unit>
<unit id="recordButton.rerecord">
<segment>
<source>RERECORD</source>
<target/>
</segment>
</unit>
<unit id="recordButton.resume">
<segment>
<source>RESUME</source>
<target/>
</segment>
</unit>
<unit id="recordStepSettings.custom">
<segment>
<source>Custom</source>
Expand Down Expand Up @@ -9436,6 +9508,12 @@
<target/>
</segment>
</unit>
<unit id="wsAudioPlayer.reset">
<segment>
<source>Reset</source>
<target/>
</segment>
</unit>
<unit id="wsAudioPlayer.resume">
<segment>
<source>Resume</source>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/public/localization/stringsbd78e002.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion src/renderer/public/localization/stringsf10b854f.json

This file was deleted.

2 changes: 1 addition & 1 deletion src/renderer/src/business/player/usePlayerLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const usePlayerLogic = (props: PlayerLogicProps) => {

useEffect(() => {
if (allowSegment)
if (suggestedSegments) {
if (suggestedSegments && segmentsRef.current !== suggestedSegments) {
segmentsRef.current = suggestedSegments;
setDefaultSegments(segmentsRef.current);
onSegment && onSegment(segmentsRef.current, true);
Expand Down
22 changes: 13 additions & 9 deletions src/renderer/src/components/App/AppHead.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import {
LinearProgress,
Tooltip,
Box,
useTheme,
useMediaQuery,
} from '@mui/material';
import HomeIcon from '@mui/icons-material/Home';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
Expand All @@ -35,6 +33,7 @@ import {
exitApp,
useMyNavigate,
useWaitForRemoteQueue,
useMobile,
} from '../../utils';
import { withBucket } from '../../hoc/withBucket';
import { usePlan } from '../../crud';
Expand All @@ -48,6 +47,7 @@ import { useHome } from '../../utils/useHome';
import { ApmLogo } from '../../control/ApmLogo';
import { OrgHead } from './OrgHead';
import { HeadStatus } from './HeadStatus';
import MobileDetailTitle from './MobileDetailTitle';

const twoIcon = { minWidth: `calc(${48 * 2}px)` } as React.CSSProperties;
const threeIcon = { minWidth: `calc(${48 * 3}px)` } as React.CSSProperties;
Expand Down Expand Up @@ -115,8 +115,7 @@ export const AppHead = (props: IProps) => {
const orbitErrorMsg = useSelector((state: IState) => state.orbit.message);
const { pathname } = useLocation();
const navigate = useMyNavigate();
const theme = useTheme();
const isMobileWidth = useMediaQuery(theme.breakpoints.down('sm'));
const { isMobileView, isMobileWidth } = useMobile();
const [home] = useGlobal('home'); //verified this is not used in a function 2/18/25
const [orgRole] = useGlobal('orgRole'); //verified this is not used in a function 2/18/25
const [errorReporter] = useGlobal('errorReporter');
Expand Down Expand Up @@ -149,7 +148,6 @@ export const AppHead = (props: IProps) => {
const [showTerms, setShowTerms] = useState('');
const waitForRemoteQueue = useWaitForRemoteQueue();
const waitForDataChangesQueue = useWaitForRemoteQueue('datachanges');
const [mobileView] = useGlobal('mobileView');

// eslint-disable-next-line react-hooks/exhaustive-deps
const saving = useMemo(() => anySaving(), [toolsChanged]);
Expand Down Expand Up @@ -284,6 +282,12 @@ export const AppHead = (props: IProps) => {
return undefined;
};

const handleTeamNav = () => {
checkSavedFn(() => navigate('/team'));
};

const handlePlanNav = () => checkSavedFn(() => navigate(planUrl || '/team'));

useEffect(() => {
window.addEventListener('beforeunload', handleUnload);
if (!user) {
Expand Down Expand Up @@ -344,7 +348,7 @@ export const AppHead = (props: IProps) => {
if (view === 'Terms') navigate('/terms');
if (view === 'Privacy') navigate('/privacy');

return !mobileView && !isMobileWidth ? (
return !isMobileView && !isMobileWidth ? (
<AppBar
position="fixed"
sx={{ width: '100%', display: 'flex' }}
Expand Down Expand Up @@ -413,15 +417,15 @@ export const AppHead = (props: IProps) => {
<>
<Toolbar>
{!isDetail ? (
<IconButton onClick={() => navigate('/team')} sx={{ p: 0 }}>
<IconButton onClick={handleTeamNav} sx={{ p: 0 }}>
<ApmLogo sx={{ width: '24px', height: '24px' }} />
</IconButton>
) : (
<IconButton onClick={() => navigate(planUrl || '/team')}>
<IconButton onClick={handlePlanNav}>
<ArrowBackIcon sx={{ width: '24px', height: '24px' }} />
</IconButton>
)}
<OrgHead />
{isDetail ? <MobileDetailTitle /> : <OrgHead />}
<GrowingSpacer />
{!isMobileWidth && (
<HeadStatus
Expand Down
Loading