Skip to content

Commit

Permalink
feat: added building cancellation feature, added building scheduling …
Browse files Browse the repository at this point in the history
…feature, refactored test env, added building interface tests
  • Loading branch information
jurerotar committed Sep 19, 2024
1 parent b829ef0 commit bbb6251
Show file tree
Hide file tree
Showing 37 changed files with 887 additions and 392 deletions.
2 changes: 1 addition & 1 deletion __mocks__/game/player-mock.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Player } from 'interfaces/models/game/player';

export const playerMock: Player = {
id: '42c53448-ab04-4ae3-8ce5-dccad55162a7',
id: 'uuid',
name: 'Player name',
faction: 'player',
tribe: 'romans',
Expand Down
4 changes: 3 additions & 1 deletion __mocks__/game/server-mock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Server } from 'interfaces/models/game/server';
import { serverFactory } from 'app/factories/server-factory';
import type { Server } from 'interfaces/models/game/server';

const MOCK_SERVER_SEED = '23223ca711';
const MOCK_SERVER_NAME = 'test server';
Expand All @@ -19,6 +19,8 @@ const mockServerConfig: Pick<Server, 'name' | 'seed' | 'configuration' | 'player

export const serverMock: Server = serverFactory(mockServerConfig);

export const serverPathMock = `/game/${serverMock.slug}`;

export const gaulServerMock: Server = {
...serverMock,
};
Expand Down
4 changes: 2 additions & 2 deletions __mocks__/game/village/village-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { id: playerId } = playerMock;

export const villageMock: Village = {
id: '0-0',
name: 'player-950bbc85d18046cbc1a87cd58ca03e24f2b0f72e',
name: 'player',
slug: 'v-1',
coordinates: {
x: 0,
Expand All @@ -17,7 +17,7 @@ export const villageMock: Village = {
resourceFieldComposition: '4446',
playerId,
isCapital: true,
lastUpdatedAt: 1703165323113,
lastUpdatedAt: Date.now(),
resources: {
wood: 750,
clay: 750,
Expand Down
87 changes: 0 additions & 87 deletions src/app/[game]/[map]/components/__tests__/map-controls.test.tsx

This file was deleted.

6 changes: 5 additions & 1 deletion src/app/[game]/[map]/components/cell.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
&-position-#{$position} {
@include imageSet('#{$resourceBase}/#{$group}/#{$position}');
@if($hasVariants) {
@for $variant from 1 through 3 {
@for $variant from 0 through 3 {
&-variant-#{$variant} {
$variantPosition: '#{$position}-#{$variant}';

@if($variant == 0) {
$variantPosition: '#{$position}';
}
@include imageSet('#{$resourceBase}/#{$group}/#{$variantPosition}');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const BuildingCategoryPanel: React.FC<BuildingCategoryPanelProps> = ({ buildingC
const hasNoAvailableBuildings = currentlyAvailableBuildings.length + currentlyUnavailableBuildings.length === 0;

return (
<div className="flex flex-col gap-4 pt-4">
<div className="flex flex-col gap-4 pt-2">
{!hasNoAvailableBuildings && (
<>
{currentlyAvailableBuildings.length > 0 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,12 @@ export const BuildingDetails: React.FC = () => {
const [tabIndex, setTabIndex] = useState<number>(tabNameToIndex[searchParams.get('tab') ?? 'default']);

return (
<article className="flex flex-col gap-4 py-2">
<article className="flex flex-col gap-2">
<Tabs
selectedIndex={tabIndex}
onSelect={(index) => setTabIndex(index)}
>
<TabList className="flex mb-4 overflow-x-scroll scrollbar-hidden">
<TabList className="flex mb-2 overflow-x-scroll scrollbar-hidden">
<StyledTab>{t('DEFAULT')}</StyledTab>
{tabs.map((name: string) => (
<StyledTab key={name}>{t(`${buildingId}.${name}`)}</StyledTab>
Expand All @@ -195,7 +195,7 @@ export const BuildingDetails: React.FC = () => {
</div>
<Suspense fallback={<>Loading tab</>}>
{!MainTabAdditionalContent ? null : (
<div className="mt-4 border border-gray-500 p-2">
<div className="mt-2 border border-gray-500 p-2">
<MainTabAdditionalContent />
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/app/[game]/[village]/[...building-field-id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ export const BuildingPage: React.FC = () => {
const hasBuilding = !!buildingField;

return (
<main className="mt-4 md:mt-24 mx-auto max-w-2xl px-2 lg:px-0">
<div className="flex flex-col gap-4">
<main className="mt-2 md:mt-24 mx-auto max-w-2xl px-2 lg:px-0 mb-14 lg:mb-0">
<div className="flex flex-col gap-2">
<Backlink />
{hasBuilding && <BuildingDetails />}
{!hasBuilding && <BuildingConstruction />}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { QueryClient } from '@tanstack/react-query';
import { screen } from '@testing-library/react';
import { BuildingActions } from 'app/[game]/[village]/components/building-actions';
import { villagesCacheKey } from 'app/[game]/hooks/use-villages';
import type { Resources } from 'interfaces/models/game/resource';
import type { BuildingField, Village } from 'interfaces/models/game/village';
import { serverPathMock } from 'mocks/game/server-mock';
import { villageMock } from 'mocks/game/village/village-mock';
import { renderWithGameContext } from 'test-utils';
import { describe } from 'vitest';

const level1MainBuildingBuildingField: BuildingField = {
buildingId: 'MAIN_BUILDING',
id: 38,
level: 1,
};

const level10MainBuildingBuildingField: BuildingField = {
buildingId: 'MAIN_BUILDING',
id: 38,
level: 10,
};

const level1CrannyBuildingField: BuildingField = {
buildingId: 'MAIN_BUILDING',
id: 37,
level: 1,
};

const level10CrannyBuildingField: BuildingField = {
buildingId: 'MAIN_BUILDING',
id: 37,
level: 10,
};

const noResources: Resources = {
wood: 0,
clay: 0,
iron: 0,
wheat: 0,
};

describe('Main building level 1 on building field with cranny already built', () => {
const queryClient = new QueryClient();

const buildingFields: BuildingField[] = [level1MainBuildingBuildingField, level1CrannyBuildingField];

const villageMockWithLevel1MainBuilding: Village = {
...villageMock,
buildingFields,
};
queryClient.setQueryData<Village[]>([villagesCacheKey], [villageMockWithLevel1MainBuilding]);

test('Upgrade button should be rendered and enabled', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const upgradeButton = screen.getByTestId('building-actions-upgrade-building-button');

expect(upgradeButton).toBeInTheDocument();
expect(upgradeButton).not.toBeDisabled();
});

test('Max level cranny should not have upgrade button rendered', () => {
const queryClient = new QueryClient();

const buildingFields: BuildingField[] = [level1MainBuildingBuildingField, level10CrannyBuildingField];

const villageMockWithLevel1MainBuilding: Village = {
...villageMock,
buildingFields,
};

queryClient.setQueryData<Village[]>([villagesCacheKey], [villageMockWithLevel1MainBuilding]);

renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const upgradeButton = screen.queryByTestId('building-actions-upgrade-building-button');

expect(upgradeButton).not.toBeInTheDocument();
});

test('Upgrade button should be rendered and enabled', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const upgradeButton = screen.getByTestId('building-actions-upgrade-building-button');

expect(upgradeButton).toBeInTheDocument();
expect(upgradeButton).not.toBeDisabled();
});

test('Downgrade and demolish buttons should not be rendered', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const downgradeButton = screen.queryByTestId('building-actions-downgrade-building-button');
const demolishButton = screen.queryByTestId('building-actions-demolish-building-button');

expect(downgradeButton).not.toBeInTheDocument();
expect(demolishButton).not.toBeInTheDocument();
});

test('Construct button should not be rendered', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const upgradeButton = screen.queryByTestId('building-actions-construct-building-button');

expect(upgradeButton).not.toBeInTheDocument();
});
});

describe('Main building level 10 on building field with cranny already built', () => {
const queryClient = new QueryClient();

const buildingFields: BuildingField[] = [level10MainBuildingBuildingField, level1CrannyBuildingField];

const villageMockWithLevel10MainBuilding: Village = {
...villageMock,
buildingFields,
};
queryClient.setQueryData<Village[]>([villagesCacheKey], [villageMockWithLevel10MainBuilding]);

test('Downgrade should not be rendered if building is level 1', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const downgradeButton = screen.queryByTestId('building-actions-downgrade-building-button');

expect(downgradeButton).not.toBeInTheDocument();
});

test('Demolish button should be rendered', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const demolishButton = screen.getByTestId('building-actions-demolish-building-button');

expect(demolishButton).toBeInTheDocument();
});
});

describe('Different building field', () => {
const queryClient = new QueryClient();

const buildingFields: BuildingField[] = [level10MainBuildingBuildingField];

const villageMockWithLevel10MainBuilding: Village = {
...villageMock,
buildingFields,
};
queryClient.setQueryData<Village[]>([villagesCacheKey], [villageMockWithLevel10MainBuilding]);

test('Only construct button should be rendered', () => {
renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/36` });
const upgradeButton = screen.queryByTestId('building-actions-upgrade-building-button');
const downgradeButton = screen.queryByTestId('building-actions-downgrade-building-button');
const constructButton = screen.getByTestId('building-actions-construct-building-button');
const demolishButton = screen.queryByTestId('building-actions-demolish-building-button');

expect(upgradeButton).not.toBeInTheDocument();
expect(downgradeButton).not.toBeInTheDocument();
expect(demolishButton).not.toBeInTheDocument();
expect(constructButton).toBeInTheDocument();
});
});

test('Construct button should be disabled with insufficient resources', () => {
const queryClient = new QueryClient();

const buildingFields: BuildingField[] = [level10MainBuildingBuildingField];

const villageMockWithLevel10MainBuilding: Village = {
...villageMock,
buildingFields,
resources: noResources,
};
queryClient.setQueryData<Village[]>([villagesCacheKey], [villageMockWithLevel10MainBuilding]);

renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/36` });
const constructButton = screen.getByTestId('building-actions-construct-building-button');

expect(constructButton).toBeInTheDocument();
expect(constructButton).toBeDisabled();
});

test('Upgrade button should be disabled with insufficient resources', () => {
const queryClient = new QueryClient();
const buildingFields: BuildingField[] = [level10MainBuildingBuildingField, level1CrannyBuildingField];

const villageMockWithLevel10MainBuilding: Village = {
...villageMock,
buildingFields,
resources: noResources,
};

queryClient.setQueryData<Village[]>([villagesCacheKey], [villageMockWithLevel10MainBuilding]);

renderWithGameContext(<BuildingActions buildingId="CRANNY" />, { queryClient, path: `${serverPathMock}/v-1/village/37` });
const upgradeButton = screen.getByTestId('building-actions-upgrade-building-button');

expect(upgradeButton).toBeInTheDocument();
expect(upgradeButton).toBeDisabled();
});
Loading

0 comments on commit bbb6251

Please sign in to comment.