mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-29 10:18:29 -04:00
* Implement AddSeedsPicker component and enhance planting selection UI; update styles and serializers for better seed management * Enhance PlantingPicker component: add hints and titles for harvest and seed actions; update test to reflect changes in seed saving behavior * Enhance harvest recording: allow harvesting from crops without plantings; update forms and modals for better user experience * Refactor code structure for improved readability and maintainability * Remove AddSeedsPicker component and its associated tests * Add planting picker functionality for harvesting and seed saving - Introduced PlantingPickersController to handle JSON responses for member's plantings. - Updated HomeHelper to include crop ID in planting picker properties. - Refactored PlantPicker and PlantSomethingModal components to support multiple gardens. - Created PlantingFlow component to manage planting actions (harvest and save seeds). - Implemented RecordDialog component to handle server-rendered links for harvesting and seed saving. - Updated various views to include data attributes for triggering the record dialog. - Added routes for planting picker and corresponding request specs for testing. - Adjusted styles in _plantings.scss for better layout and spacing. * Add job management features including job creation and wishlist functionality * Remove WishlistJobsController and associated views and tests * Add suggestion links rendering and fix routes formatting * Refactor AddJobModal and JobPicker components to simplify props and enhance user experience * Update seed addition test to check dialog presence and current path * Apply suggestion from @CloCkWeRX --------- Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com>
140 lines
5.2 KiB
React
140 lines
5.2 KiB
React
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}) => <button type="button" onClick={() => onChoose({id: 5, name: 'Tomato'})}>Pick tomato</button>,
|
|
}));
|
|
|
|
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(<PlantSomethingModal garden={garden} onClose={vi.fn()} onCreated={onCreated} {...props} />);
|
|
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);
|
|
});
|
|
});
|
|
});
|