import {render, screen} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {describe, expect, it, vi} from 'vitest';
import {daysBefore, localToday} from '../dates';
import PlantSomethingModal from './PlantSomethingModal';
vi.mock('./CropPicker', () => ({
default: ({onChoose}) => ,
}));
const garden = {id: 3, name: 'Back yard'};
function stubFetch(response) {
const fetch = vi.fn(async () => ({ok: response.ok, status: response.status, json: async () => response.data || {}}));
vi.stubGlobal('fetch', fetch);
return fetch;
}
function setup(props = {}) {
const onCreated = vi.fn();
render();
return onCreated;
}
const plant = () => userEvent.click(screen.getByRole('button', {name: 'Plant it'}));
describe('PlantSomethingModal', () => {
it('is titled for the garden', () => {
setup();
expect(screen.getByRole('dialog', {name: 'Plant something in Back yard'})).toBeInTheDocument();
});
it('starts by choosing a crop', () => {
setup();
expect(screen.getByRole('button', {name: 'Pick tomato'})).toBeInTheDocument();
});
it('has no Plant it button until a crop is chosen', () => {
setup();
expect(screen.queryByRole('button', {name: 'Plant it'})).not.toBeInTheDocument();
});
it('asks to confirm the chosen crop', async () => {
setup();
await userEvent.click(screen.getByRole('button', {name: 'Pick tomato'}));
expect(screen.getByText('Ready to plant?')).toBeInTheDocument();
});
it('goes back to choosing with Change', async () => {
setup();
await userEvent.click(screen.getByRole('button', {name: 'Pick tomato'}));
await userEvent.click(screen.getByRole('button', {name: /Change/}));
expect(screen.getByRole('button', {name: 'Pick tomato'})).toBeInTheDocument();
});
it('starts on the confirm step when given a crop', () => {
setup({crop: {id: 5, name: 'Kale'}});
expect(screen.getByText('Kale')).toBeInTheDocument();
});
it('focuses Plant it once a crop is chosen', async () => {
setup();
await userEvent.click(screen.getByRole('button', {name: 'Pick tomato'}));
expect(screen.getByRole('button', {name: 'Plant it'})).toHaveFocus();
});
it('leaves today to the server', async () => {
const fetch = stubFetch({ok: true, status: 201, data: {garden: {}}});
setup({crop: {id: 5, name: 'Tomato'}});
await plant();
expect(fetch).toHaveBeenCalledWith('/plantings.json', expect.objectContaining({
body: JSON.stringify({planting: {garden_id: 3, crop_id: 5}}),
}));
});
it('sends yesterday\'s date', async () => {
const fetch = stubFetch({ok: true, status: 201, data: {garden: {}}});
setup({crop: {id: 5, name: 'Tomato'}});
await userEvent.click(screen.getByLabelText('Yesterday'));
await plant();
expect(JSON.parse(fetch.mock.calls[0][1].body).planting.planted_at).toBe(daysBefore(localToday(), 1));
});
it('hands the garden and crop to onCreated', async () => {
stubFetch({ok: true, status: 201, data: {garden: {id: 3}}});
const onCreated = setup({crop: {id: 5, name: 'Tomato'}});
await plant();
expect(onCreated).toHaveBeenCalledWith({id: 3}, {id: 5, name: 'Tomato'});
});
it('lists the server\'s reasons', async () => {
stubFetch({ok: false, status: 422, data: {errors: {crop: ['is not allowed']}}});
setup({crop: {id: 5, name: 'Tomato'}});
await plant();
expect(await screen.findByText('Crop is not allowed')).toBeInTheDocument();
});
it('says when it is not allowed', async () => {
stubFetch({ok: false, status: 403});
setup({crop: {id: 5, name: 'Tomato'}});
await plant();
expect(await screen.findByText('You can\'t plant in this garden.')).toBeInTheDocument();
});
it('says when the server cannot be reached', async () => {
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('offline')));
setup({crop: {id: 5, name: 'Tomato'}});
await plant();
expect(await screen.findByText(/Couldn't reach the server/)).toBeInTheDocument();
});
describe('with several gardens', () => {
const gardens = [{id: 3, name: 'Back yard'}, {id: 4, name: 'Balcony'}];
const chooseCrop = () => userEvent.click(screen.getByRole('button', {name: 'Pick tomato'}));
it('asks which garden after the crop', async () => {
setup({garden: undefined, gardens});
await chooseCrop();
expect(screen.getByRole('button', {name: 'Balcony'})).toBeInTheDocument();
});
it('has no Plant it button until a garden is chosen', async () => {
setup({garden: undefined, gardens});
await chooseCrop();
expect(screen.queryByRole('button', {name: 'Plant it'})).not.toBeInTheDocument();
});
it('plants in the garden chosen', async () => {
const fetch = stubFetch({ok: true, data: {garden: {}}});
setup({garden: undefined, gardens});
await chooseCrop();
await userEvent.click(screen.getByRole('button', {name: 'Balcony'}));
await plant();
expect(JSON.parse(fetch.mock.calls[0][1].body).planting.garden_id).toBe(4);
});
});
});