import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {describe, expect, it, vi} from 'vitest';
import EditPlantingModal from './EditPlantingModal';
vi.mock('./CropPicker', () => ({
default: ({onChoose}) => ,
}));
const planting = {id: 1, url: '/plantings/tomato', crop: {name: 'Tomato'}};
const form = {
planting: {
crop: {id: 5, name: 'Tomato'}, planted_at: '2025-05-01', planted_from: 'seed', sunniness: 'sun', quantity: 3,
overall_rating: 4, description: 'Doing well', finished: false, finished_at: '', failed: false,
},
planted_from_values: ['seed', 'seedling'],
sunniness_values: ['sun', 'shade'],
};
function stubFetch(...replies) {
const fetch = vi.fn();
replies.forEach((reply) => fetch.mockResolvedValueOnce({ok: reply.ok ?? true, status: reply.status ?? 200, json: async () => reply.data ?? {}}));
vi.stubGlobal('fetch', fetch);
return fetch;
}
function setup() {
const onSaved = vi.fn();
render();
return onSaved;
}
describe('EditPlantingModal', () => {
it('is titled for the crop', () => {
stubFetch({data: form});
setup();
expect(screen.getByRole('dialog', {name: 'Edit Tomato'})).toBeInTheDocument();
});
it('shows it is loading', () => {
stubFetch({data: form});
setup();
expect(screen.getByRole('status')).toHaveTextContent('Loading…');
});
it('loads the planting\'s edit form', async () => {
const fetch = stubFetch({data: form});
setup();
await screen.findByLabelText('When?');
expect(fetch).toHaveBeenCalledWith('/plantings/tomato/edit.json', expect.anything());
});
it('fills in the current values', async () => {
stubFetch({data: form});
setup();
expect(await screen.findByLabelText('Tell us more about it')).toHaveValue('Doing well');
});
it('shows the rating', async () => {
stubFetch({data: form});
setup();
expect(await screen.findByRole('radio', {name: '4 of 5'})).toBeChecked();
});
it('has no finished date until finished is ticked', async () => {
stubFetch({data: form});
setup();
await screen.findByLabelText('When?');
expect(screen.queryByLabelText('Finished date')).not.toBeInTheDocument();
});
it('asks for a finished date once finished is ticked', async () => {
stubFetch({data: form});
setup();
await userEvent.click(await screen.findByLabelText('Mark as finished'));
expect(screen.getByLabelText('Finished date')).toBeInTheDocument();
});
it('clears the finished date when finished is unticked', async () => {
const fetch = stubFetch({data: {...form, planting: {...form.planting, finished: true, finished_at: '2025-06-01'}}}, {data: {garden: {}}});
setup();
await userEvent.click(await screen.findByLabelText('Mark as finished'));
await userEvent.click(screen.getByRole('button', {name: 'Save'}));
expect(JSON.parse(fetch.mock.calls[1][1].body).planting.finished_at).toBe('');
});
it('changes the crop', async () => {
const fetch = stubFetch({data: form}, {data: {garden: {}}});
setup();
await userEvent.click(await screen.findByRole('button', {name: /Change/}));
await userEvent.click(screen.getByRole('button', {name: 'Pick kale'}));
await userEvent.click(screen.getByRole('button', {name: 'Save'}));
expect(JSON.parse(fetch.mock.calls[1][1].body).planting.crop_id).toBe(8);
});
it('patches the planting', async () => {
const fetch = stubFetch({data: form}, {data: {garden: {}}});
setup();
await userEvent.click(await screen.findByRole('button', {name: 'Save'}));
expect(fetch).toHaveBeenLastCalledWith('/plantings/tomato', expect.objectContaining({method: 'PATCH'}));
});
it('hands the refreshed garden to onSaved', async () => {
stubFetch({data: form}, {data: {garden: {id: 4}}});
const onSaved = setup();
await userEvent.click(await screen.findByRole('button', {name: 'Save'}));
expect(onSaved).toHaveBeenCalledWith({id: 4});
});
it('lists the server\'s reasons', async () => {
stubFetch({data: form}, {ok: false, status: 422, data: {errors: {quantity: ['must be positive']}}});
setup();
await userEvent.click(await screen.findByRole('button', {name: 'Save'}));
expect(await screen.findByText('Quantity must be positive')).toBeInTheDocument();
});
it('says when it could not be loaded', async () => {
stubFetch({ok: false, status: 500});
setup();
expect(await screen.findByRole('alert')).toHaveTextContent('Couldn’t load this planting');
});
});