createNodePopoverBoard.test.tsx5.0 KBView on GitHub /**
* "New → Board" in the file tree.
*
* The gap this closes: `#board` in the playbook editor was the ONLY way to make a board, so a
* board existed if an agent-authoring surface happened to be open and not otherwise.
*
* The one thing worth asserting beyond "the mutation fires": a board does NOT land at the
* cursor's folder. `createBoard` puts every board at `<scope>/boards/<slug>` because a board is
* addressed BY NAME and `grantsBoard` is exact rather than prefix-covering. So the popover
* creates into the tree's SCOPE and says where it went — a preview that lied about the path
* would be worse than no preview.
*/
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
const mockCreateBoard = jest.fn();
jest.mock('@/providers/query-provider', () => ({
useTRPC: () => ({
boards: {
templates: { queryOptions: () => ({ queryKey: ['boards.templates'] }) },
create: { mutationOptions: () => ({ mutationKey: ['boards.create'] }) },
},
files: {
createFile: { mutationOptions: () => ({ mutationKey: ['files.createFile'] }) },
createFolder: { mutationOptions: () => ({ mutationKey: ['files.createFolder'] }) },
},
kb: {
createKbFromUrl: { mutationOptions: () => ({ mutationKey: ['kb.url'] }) },
createKbFromGoogleDrive: { mutationOptions: () => ({ mutationKey: ['kb.drive'] }) },
},
}),
trpcClient: {},
}));
jest.mock('@tanstack/react-query', () => ({
useQueryClient: () => ({ invalidateQueries: jest.fn() }),
useQuery: (opts: { queryKey=[redacted] }) =>
opts.queryKey[0] === 'boards.templates'
? {
data: {
templates: [
{ id: 'customer-requests', name: 'Customer requests', description: 'x', emoji: '🗺️' },
{ id: 'work-items', name: 'Work items', description: 'y', emoji: '📋' },
],
},
}
: { data: undefined },
useMutation: (opts: { mutationKey=[redacted] }) => ({
mutate: opts.mutationKey[0] === 'boards.create' ? mockCreateBoard : jest.fn(),
isPending: false,
}),
}));
jest.mock('@/modules/store', () => ({
useCedarStore: Object.assign((sel: (s: unknown) => unknown) => sel({}), {
getState: () => ({ setDocument: jest.fn() }),
}),
useAdminViewingUserId: () => null,
}));
jest.mock('@/modules/files/upload/useFileUpload', () => ({
useFileUpload: () => ({ uploadToFolder: jest.fn() }),
}));
jest.mock('@/modules/integrations/use-google-picker', () => ({
useGooglePicker: () => ({ openPicker: jest.fn(), isLoading: false, isAvailable: false }),
}));
import { CreateNodePopover } from '@/modules/company/components/CompanyExplorer';
function open(scope: { type: 'org' | 'user'; id: string }) {
render(<CreateNodePopover scope={scope} parentId="some-folder-the-cursor-is-on" label="Actions" />);
// The `⋮` opens a picker of "New …" rows; picking one is what opens the form behind it.
fireEvent.click(screen.getByLabelText('Actions'));
fireEvent.click(screen.getByText('New board'));
}
beforeEach(() => mockCreateBoard.mockClear());
describe('New → Board', () => {
it('creates into the tree SCOPE with the chosen template, not into the selected folder', () => {
open({ type: 'org', id: 'org-1' });
fireEvent.change(screen.getByPlaceholderText('Board name'), {
target: { value: 'Customer feedback' },
});
fireEvent.change(screen.getByLabelText('Start from'), {
target: { value: 'customer-requests' },
});
fireEvent.click(screen.getByText('Save'));
expect(mockCreateBoard).toHaveBeenCalledWith({
name: 'Customer feedback',
scope: 'org',
template: 'customer-requests',
});
// `parentId` is deliberately absent: `boards.create` has no such input, and a board that
// honoured it would have no name to grant.
expect(mockCreateBoard.mock.calls[0][0]).not.toHaveProperty('parentId');
});
it('sends no template at all for an empty board', () => {
open({ type: 'user', id: 'user-1' });
fireEvent.change(screen.getByPlaceholderText('Board name'), { target: { value: 'Scratch' } });
fireEvent.click(screen.getByText('Save'));
expect(mockCreateBoard).toHaveBeenCalledWith({ name: 'Scratch', scope: 'user' });
});
it('says where the board will land, using the server\'s own slug rule', () => {
open({ type: 'org', id: 'org-1' });
expect(
screen.getByText('Boards live in organisation/boards, not in the selected folder.'),
).toBeInTheDocument();
fireEvent.change(screen.getByPlaceholderText('Board name'), {
target: { value: 'Customer Feedback!!' },
});
expect(
screen.getByText('Goes to organisation/boards/customer-feedback'),
).toBeInTheDocument();
});
it('offers no file upload on the board branch — there is nothing to upload into', () => {
// Upload is a row in the picker, not a button under the form: once "New board" has been
// chosen there is nothing on screen inviting you to upload into a board.
open({ type: 'org', id: 'org-1' });
expect(screen.queryByText('Upload files')).not.toBeInTheDocument();
});
});