fix(extensions): reset stale skill review state on close
This commit is contained in:
parent
2142518461
commit
2201b54f28
4 changed files with 173 additions and 2 deletions
|
|
@ -219,6 +219,18 @@ export const SkillEditorDialog = ({
|
|||
setMutationError(null);
|
||||
}, [detail, mode, open, projectPath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
return;
|
||||
}
|
||||
|
||||
setReviewPreview(null);
|
||||
setReviewOpen(false);
|
||||
setReviewLoading(false);
|
||||
setSaveLoading(false);
|
||||
setMutationError(null);
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && mode === 'create' && scope === 'project' && !projectPath) {
|
||||
setScope('user');
|
||||
|
|
|
|||
|
|
@ -93,6 +93,18 @@ export const SkillImportDialog = ({
|
|||
setMutationError(null);
|
||||
}, [open, projectPath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPreview(null);
|
||||
setReviewOpen(false);
|
||||
setReviewLoading(false);
|
||||
setImportLoading(false);
|
||||
setMutationError(null);
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || folderNameEdited) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ vi.mock('@renderer/components/extensions/skills/SkillCodeEditor', () => ({
|
|||
}));
|
||||
|
||||
vi.mock('@renderer/components/extensions/skills/SkillReviewDialog', () => ({
|
||||
SkillReviewDialog: () => null,
|
||||
SkillReviewDialog: ({ open }: { open: boolean }) =>
|
||||
open ? React.createElement('div', { 'data-testid': 'skill-review-dialog' }, 'Review') : null,
|
||||
}));
|
||||
|
||||
vi.mock('lucide-react', () => {
|
||||
|
|
@ -242,4 +243,74 @@ This file uses a freeform layout without generated sections.
|
|||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it('clears review state when the editor closes externally', async () => {
|
||||
const host = document.createElement('div');
|
||||
document.body.appendChild(host);
|
||||
const root = createRoot(host);
|
||||
storeState.previewSkillUpsert.mockResolvedValue({
|
||||
planId: 'plan-1',
|
||||
targetSkillDir: '/tmp/project/.claude/skills/new-skill',
|
||||
changes: [
|
||||
{
|
||||
relativePath: 'SKILL.md',
|
||||
absolutePath: '/tmp/project/.claude/skills/new-skill/SKILL.md',
|
||||
action: 'create',
|
||||
oldContent: null,
|
||||
newContent: '# Skill',
|
||||
isBinary: false,
|
||||
},
|
||||
],
|
||||
warnings: [],
|
||||
summary: { created: 1, updated: 0, deleted: 0, binary: 0 },
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
React.createElement(SkillEditorDialog, {
|
||||
open: true,
|
||||
mode: 'create',
|
||||
projectPath: '/tmp/project',
|
||||
projectLabel: 'Project',
|
||||
detail: null,
|
||||
onClose: vi.fn(),
|
||||
onSaved: vi.fn(),
|
||||
})
|
||||
);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const reviewButton = Array.from(host.querySelectorAll('button')).find((button) =>
|
||||
button.textContent?.includes('Review And Create')
|
||||
) as HTMLButtonElement;
|
||||
await act(async () => {
|
||||
reviewButton.click();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(host.querySelector('[data-testid="skill-review-dialog"]')).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
React.createElement(SkillEditorDialog, {
|
||||
open: false,
|
||||
mode: 'create',
|
||||
projectPath: '/tmp/project',
|
||||
projectLabel: 'Project',
|
||||
detail: null,
|
||||
onClose: vi.fn(),
|
||||
onSaved: vi.fn(),
|
||||
})
|
||||
);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(host.querySelector('[data-testid="skill-review-dialog"]')).toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -93,7 +93,8 @@ vi.mock('@renderer/components/ui/select', () => ({
|
|||
}));
|
||||
|
||||
vi.mock('@renderer/components/extensions/skills/SkillReviewDialog', () => ({
|
||||
SkillReviewDialog: () => null,
|
||||
SkillReviewDialog: ({ open }: { open: boolean }) =>
|
||||
open ? React.createElement('div', { 'data-testid': 'skill-review-dialog' }, 'Review') : null,
|
||||
}));
|
||||
|
||||
vi.mock('lucide-react', () => {
|
||||
|
|
@ -302,4 +303,79 @@ describe('SkillImportDialog', () => {
|
|||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
|
||||
it('clears review state when the import dialog closes externally', async () => {
|
||||
const host = document.createElement('div');
|
||||
document.body.appendChild(host);
|
||||
const root = createRoot(host);
|
||||
|
||||
storeState.previewSkillImport.mockResolvedValue({
|
||||
planId: 'plan-1',
|
||||
targetSkillDir: '/tmp/imported-skill',
|
||||
changes: [
|
||||
{
|
||||
relativePath: 'SKILL.md',
|
||||
absolutePath: '/tmp/imported-skill/SKILL.md',
|
||||
action: 'create',
|
||||
oldContent: null,
|
||||
newContent: '# Skill',
|
||||
isBinary: false,
|
||||
},
|
||||
],
|
||||
warnings: [],
|
||||
summary: { created: 1, updated: 0, deleted: 0, binary: 0 },
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
React.createElement(SkillImportDialog, {
|
||||
open: true,
|
||||
projectPath: null,
|
||||
projectLabel: null,
|
||||
onClose: vi.fn(),
|
||||
onImported: vi.fn(),
|
||||
})
|
||||
);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const sourceInput = host.querySelector('#skill-import-source') as HTMLInputElement;
|
||||
await act(async () => {
|
||||
const setValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
|
||||
setValue?.call(sourceInput, '/tmp/source-skill');
|
||||
sourceInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
const reviewButton = Array.from(host.querySelectorAll('button')).find((button) =>
|
||||
button.textContent?.includes('Review And Import')
|
||||
) as HTMLButtonElement;
|
||||
await act(async () => {
|
||||
reviewButton.click();
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(host.querySelector('[data-testid="skill-review-dialog"]')).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
React.createElement(SkillImportDialog, {
|
||||
open: false,
|
||||
projectPath: null,
|
||||
projectLabel: null,
|
||||
onClose: vi.fn(),
|
||||
onImported: vi.fn(),
|
||||
})
|
||||
);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(host.querySelector('[data-testid="skill-review-dialog"]')).toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
await Promise.resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue