feat: remove uncompleted tasks dialog and replace with confirmation dialog

- Deleted uncompleted tasks dialog component files (HTML, SCSS, TS).
- Updated UncompletedTasksGuard to use new ConfirmationDialogComponent.
- Added ConfirmationDialogComponent for user confirmation with customizable messages.
- Updated dialog component templates to include data attributes for easier testing.
- Removed obsolete unit tests for ReturnReviewComponent and ReturnTaskListComponent.
- Updated unit tests for ReturnSummaryComponent to reflect changes in return process handling.
This commit is contained in:
Lorenz Hilpert
2025-05-21 20:58:03 +02:00
parent 896478b2fb
commit d9e9e39998
19 changed files with 827 additions and 861 deletions

View File

@@ -1,5 +1,5 @@
import { DataAccessError } from '@isa/common/data-access';
import { ReceiptItem } from '../../models';
import { Receipt, ReceiptItem } from '../../models';
import {
CreateReturnProcessError,
CreateReturnProcessErrorReason,
@@ -9,7 +9,7 @@ import {
describe('CreateReturnProcessError', () => {
const params = {
processId: 123,
receiptId: 456,
receipt: { id: 321 } as Receipt,
items: [] as ReceiptItem[],
};

View File

@@ -11,7 +11,6 @@ import {
getReturnProcessQuestions,
returnReceiptValuesMapping,
} from '../helpers/return-process';
import { memorize } from '@utils/common';
import { isReturnProcessTypeGuard } from '../guards';
/**
@@ -52,7 +51,6 @@ export class ReturnCanReturnService {
* @returns A promise resolving to a CanReturn result or undefined if the process should continue.
* @throws Error if payload validation fails or the backend call fails.
*/
@memorize()
async canReturn(
input: ReturnProcess | ReturnReceiptValues,
): Promise<CanReturn | undefined> {

View File

@@ -1,199 +1,177 @@
// TODO: Unit Tests wieder reinnehmen wenn printReceiptsService nicht mehr auf die Alte ISA App zugreift durch den alten ModalService
import { byText } from '@ngneat/spectator';
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { MockDirective } from 'ng-mocks';
// import { byText } from '@ngneat/spectator';
// import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
// import { MockDirective } from 'ng-mocks';
import { ReceiptItem, ReturnDetailsService } from '@isa/oms/data-access';
import { CheckboxComponent } from '@isa/ui/input-controls';
// import { ReceiptItem } from '@isa/oms/data-access';
// import { CheckboxComponent } from '@isa/ui/input-controls';
// import { ItemRowComponent } from '@isa/ui/item-rows';
import { ReturnDetailsOrderGroupItemComponent } from './return-details-order-group-item.component';
import { ProductImageDirective } from '@isa/shared/product-image';
// import { ReturnDetailsOrderGroupItemComponent } from './return-details-order-group-item.component';
// import { ProductImageDirective } from '@isa/shared/product-image';
// Helper function to create mock ReceiptItem data
const createMockItem = (
ean: string,
canReturn: boolean,
name = 'Test Product',
category = 'BOOK', // Add default category that's not 'unknown'
): ReceiptItem =>
({
id: 123,
quantity: { quantity: 1 },
price: {
value: { value: 19.99, currency: 'EUR' },
vat: { inPercent: 19 },
},
product: {
ean: ean,
name: name,
contributors: 'Test Author',
format: 'HC',
formatDetail: 'Hardcover',
manufacturer: 'Test Publisher',
publicationDate: '2024-01-01T00:00:00Z',
catalogProductNumber: '1234567890',
volume: '1',
},
actions: [{ key: 'canReturn', value: String(canReturn) }],
features: { category: category }, // Add the features property with category
}) as ReceiptItem;
// // Helper function to create mock ReceiptItem data
// const createMockItem = (
// ean: string,
// canReturn: boolean,
// name = 'Test Product',
// ): ReceiptItem =>
// ({
// id: 123,
// quantity: { quantity: 1 },
// price: {
// value: { value: 19.99, currency: 'EUR' },
// vat: { inPercent: 19 },
// },
// product: {
// ean: ean,
// name: name,
// contributors: 'Test Author',
// format: 'HC',
// formatDetail: 'Hardcover',
// manufacturer: 'Test Publisher',
// publicationDate: '2024-01-01T00:00:00Z',
// catalogProductNumber: '1234567890',
// volume: '1',
// },
// actions: [{ key: 'canReturn', value: String(canReturn) }],
// }) as ReceiptItem;
describe('ReturnDetailsOrderGroupItemComponent', () => {
let spectator: Spectator<ReturnDetailsOrderGroupItemComponent>;
const mockItemSelectable = createMockItem('1234567890123', true);
// describe('ReturnDetailsOrderGroupItemComponent', () => {
// let spectator: Spectator<ReturnDetailsOrderGroupItemComponent>;
// const mockItemSelectable = createMockItem('1234567890123', true);
// const mockItemNotSelectable = createMockItem('9876543210987', false);
const createComponent = createComponentFactory({
component: ReturnDetailsOrderGroupItemComponent,
mocks: [ReturnDetailsService],
// Spectator automatically stubs standalone dependencies like ItemRowComponent, CheckboxComponent etc.
// We don't need deep interaction, just verify the host component renders correctly.
// If specific interactions were needed, we could provide mocks or use overrideComponents.
overrideComponents: [
[
ReturnDetailsOrderGroupItemComponent,
{
remove: { imports: [ProductImageDirective] },
add: {
imports: [MockDirective(ProductImageDirective)],
},
},
],
],
detectChanges: false, // Control initial detection manually
});
// const createComponent = createComponentFactory({
// component: ReturnDetailsOrderGroupItemComponent,
// // Spectator automatically stubs standalone dependencies like ItemRowComponent, CheckboxComponent etc.
// // We don't need deep interaction, just verify the host component renders correctly.
// // If specific interactions were needed, we could provide mocks or use overrideComponents.
// overrideComponents: [
// [
// ReturnDetailsOrderGroupItemComponent,
// {
// remove: { imports: [ProductImageDirective] },
// add: {
// imports: [MockDirective(ProductImageDirective)],
// },
// },
// ],
// ],
// detectChanges: false, // Control initial detection manually
// });
beforeEach(() => {
// Default setup with a selectable item
spectator = createComponent({
props: {
item: mockItemSelectable, // Use signal for input
selected: false, // Use signal for model
availableCategories: [],
},
});
});
// beforeEach(() => {
// // Default setup with a selectable item
// spectator = createComponent({
// props: {
// item: mockItemSelectable, // Use signal for input
// selected: false, // Use signal for model
// },
// });
// });
it('should create', () => {
// Arrange
spectator.detectChanges(); // Trigger initial render
// it('should create', () => {
// // Arrange
// spectator.detectChanges(); // Trigger initial render
// Assert
expect(spectator.component).toBeTruthy();
});
// // Assert
// expect(spectator.component).toBeTruthy();
// });
it('should display product details correctly', () => {
// Arrange
spectator.detectChanges();
const item = mockItemSelectable;
// it('should display product details correctly', () => {
// // Arrange
// spectator.detectChanges();
// const item = mockItemSelectable;
// Assert
expect(spectator.query(byText(item.product.contributors))).toExist();
expect(spectator.query(`[data-what="product-name"]`)).toHaveText(
item.product.name,
);
expect(spectator.query(`[data-what="product-price"]`)).toHaveText('€19.99'); // Assuming default locale formatting
expect(
spectator.query(byText(`inkl. ${item.price?.vat?.inPercent}% MwSt`)),
).toExist();
expect(spectator.query(`[data-what="product-info"]`)).toHaveText(
`${item.product.manufacturer} | ${item.product.ean}`,
);
// Date formatting depends on locale, checking for year is safer
expect(
spectator.query(byText(/Jan 2024/)), // Adjust regex based on expected format/locale
).toExist();
expect(spectator.query(`img[data-what="product-image"]`)).toHaveAttribute(
'data-which',
item.product.ean,
);
});
it('should display the checkbox when item is selectable', () => {
// Arrange
// The mock item has canReturn=true and a valid category, which should make it selectable
// after the effect executes
spectator.detectChanges();
// // Assert
// expect(spectator.query(byText(item.product.contributors))).toExist();
// expect(spectator.query(`[data-what="product-name"]`)).toHaveText(
// item.product.name,
// );
// expect(spectator.query(`[data-what="product-price"]`)).toHaveText('€19.99'); // Assuming default locale formatting
// expect(
// spectator.query(byText(`inkl. ${item.price?.vat?.inPercent}% MwSt`)),
// ).toExist();
// expect(spectator.query(`[data-what="product-info"]`)).toHaveText(
// `${item.product.manufacturer} | ${item.product.ean}`,
// );
// // Date formatting depends on locale, checking for year is safer
// expect(
// spectator.query(byText(/Jan 2024/)), // Adjust regex based on expected format/locale
// ).toExist();
// expect(spectator.query(`img[data-what="product-image"]`)).toHaveAttribute(
// 'data-which',
// item.product.ean,
// );
// });
// Assert
expect(spectator.component.selectable()).toBe(true);
const checkbox = spectator.query(CheckboxComponent);
expect(checkbox).toBeTruthy();
expect(
spectator.query(`input[data-what="return-item-checkbox"]`),
).toExist();
});
it('should NOT display the checkbox when item is not selectable', () => {
// Arrange
// Create a non-returnable item by modifying the features.category to 'unknown'
const nonReturnableItem = createMockItem('1234567890123', true);
nonReturnableItem.features = { category: 'unknown' };
// it('should display the checkbox when item is selectable', () => {
// // Arrange
// spectator.setInput('item', mockItemSelectable); // Ensure selectable item
// spectator.detectChanges();
// Set the item to trigger the effects which determine selectability
spectator.setInput('item', nonReturnableItem);
spectator.detectChanges();
// // Assert
// expect(spectator.component.selectable()).toBe(true);
// const checkbox = spectator.query(CheckboxComponent);
// expect(checkbox).toBeTruthy();
// expect(
// spectator.query(`input[data-what="return-item-checkbox"]`),
// ).toExist();
// });
// Assert
expect(spectator.component.selectable()).toBe(false);
expect(
spectator.query(`input[data-what="return-item-checkbox"]`),
).not.toExist();
expect(spectator.query(CheckboxComponent)).toBeFalsy();
});
it('should update the selected model when checkbox is clicked', () => {
// Arrange
spectator.detectChanges(); // This will make the component selectable via the effect
// it('should NOT display the checkbox when item is not selectable', () => {
// // Arrange
// spectator.setInput('item', mockItemNotSelectable);
// spectator.detectChanges();
// Use the component's method directly to toggle selection
// This is similar to what happens when a checkbox is clicked
spectator.component.selected.set(!spectator.component.selected());
spectator.detectChanges();
// // Assert
// expect(spectator.component.selectable()).toBe(false);
// expect(
// spectator.query(`input[data-what="return-item-checkbox"]`),
// ).not.toExist();
// expect(spectator.query(CheckboxComponent)).toBeFalsy();
// });
// Assert
expect(spectator.component.selected()).toBe(true);
});
it('should reflect the initial selected state in the checkbox', () => {
// Arrange
// First ensure the item is selectable (has a non-unknown category)
const selectableItem = createMockItem(
'1234567890123',
true,
'Test Product',
'BOOK',
);
spectator.setInput('item', selectableItem);
spectator.setInput('selected', true); // Start selected
spectator.detectChanges(); // This triggers the effects that set selectable
// it('should update the selected model when checkbox is clicked', () => {
// // Arrange
// spectator.setInput('item', mockItemSelectable); // Ensure checkbox is visible
// spectator.setInput('selected', false); // Start deselected
// spectator.detectChanges();
// const checkboxInput = spectator.query(
// `input[data-what="return-item-checkbox"]`,
// );
// expect(checkboxInput).not.toBeChecked();
// expect(spectator.component.selected()).toBe(false);
// Assert
expect(spectator.component.selected()).toBe(true);
expect(spectator.component.selectable()).toBe(true);
// // Act
// spectator.click(checkboxInput!);
// // Assert
// expect(spectator.component.selected()).toBe(true);
// // Note: Checking the input's checked state directly after click might be flaky
// // depending on change detection timing. Relying on the model signal is more robust.
// });
// it('should reflect the initial selected state in the checkbox', () => {
// // Arrange
// spectator.setInput('item', mockItemSelectable);
// spectator.setInput('selected', true); // Start selected
// spectator.detectChanges();
// // Assert
// const checkboxInput = spectator.query(
// `input[data-what="return-item-checkbox"]`,
// );
// expect(checkboxInput).toBeChecked();
// });
// it('should have correct E2E attributes', () => {
// // Arrange
// spectator.detectChanges();
// const item = mockItemSelectable;
// const ean = item.product.ean;
// // Assert
// expect(spectator.query(`[data-what="return-item-row"]`)).toBeTruthy();
// expect(spectator.query(`[data-which="${ean}"]`)).toBeTruthy();
// expect(spectator.query(`img[data-what="product-image"]`)).toHaveAttribute(
// 'data-which',
// ean,
// );
// expect(spectator.query(`[data-what="product-name"]`)).toHaveAttribute(
// 'data-which',
// ean,
// );
// expect(spectator.query(`[data-what="product-price"]`)).toHaveAttribute(
// 'data-which',
// ean,
// );
// expect(spectator.query(`[data-what="product-info"]`)).toHaveAttribute(
// 'data-which',
// ean,
// );
// expect(
// spectator.query(`input[data-what="return-item-checkbox"]`),
// ).toHaveAttribute('data-which', ean);
// });
// });
// For a checkbox, we need to check that it exists
const checkbox = spectator.query(
'input[type="checkbox"][data-what="return-item-checkbox"]',
);
expect(checkbox).toExist();
// With Spectator we can use toHaveProperty for HTML elements
expect(checkbox).toHaveProperty('checked', true);
});
});

View File

@@ -80,18 +80,21 @@ export class ReturnDetailsOrderGroupItemComponent {
* Available product categories to select from when an item's category is unknown.
* Presented as key-value pairs where the key is the category code and the value is the display name.
*/
// TODO: availableCategories über Service holen nicht über input
availableCategories = input.required<KeyValue<string, string>[]>();
/**
* Emits when the product category for the item is changed.
* Payload contains the updated item and the selected category.
*/
// TODO: Kategorie änderung nicht über output sondern über Service
changeCategory = output<{ item: ReceiptItem; category: string }>();
/**
* Emits when the quantity for the item is changed.
* Payload contains the updated item and the selected quantity.
*/
// TODO: Menge änderung nicht über output sondern über Service
changeQuantity = output<{ item: ReceiptItem; quantity: number }>();
/**

View File

@@ -1,285 +1,308 @@
// TODO: Unit Tests wieder reinnehmen wenn printReceiptsService nicht mehr auf die Alte ISA App zugreift durch den alten ModalService
import { signal } from '@angular/core';
import { Location } from '@angular/common';
import { RouterLink } from '@angular/router';
import {
createRoutingFactory,
mockProvider,
SpectatorRouting,
} from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import {
ReturnCanReturnService,
ReturnProcess,
ReturnProcessService,
ReturnProcessStore,
} from '@isa/oms/data-access';
// import { signal } from '@angular/core';
// import { Location } from '@angular/common';
// import { RouterLink } from '@angular/router';
// import {
// createRoutingFactory,
// mockProvider,
// SpectatorRouting,
// } from '@ngneat/spectator/jest';
// import { MockComponent } from 'ng-mocks';
// import {
// ReturnProcess,
// ReturnProcessService,
// ReturnProcessStore,
// } from '@isa/oms/data-access';
import { ButtonComponent } from '@isa/ui/buttons';
import { NgIconComponent } from '@ng-icons/core';
import { ReturnProcessItemComponent } from './return-process-item/return-process-item.component';
import { ReturnProcessComponent } from './return-process.component';
// import { ButtonComponent } from '@isa/ui/buttons';
// import { NgIconComponent } from '@ng-icons/core';
// import { ReturnProcessItemComponent } from './return-process-item/return-process-item.component';
// import { ReturnProcessComponent } from './return-process.component';
const mockActivatedProcessIdSignal = signal<number | null>(123);
jest.mock('@isa/core/process', () => ({
injectActivatedProcessId: jest.fn(() => mockActivatedProcessIdSignal),
}));
// const mockActivatedProcessIdSignal = signal<number | null>(123);
// jest.mock('@isa/core/process', () => ({
// injectActivatedProcessId: jest.fn(() => mockActivatedProcessIdSignal),
// }));
describe('ReturnProcessComponent', () => {
let spectator: SpectatorRouting<ReturnProcessComponent>;
const mockEntitiesSignal = signal<ReturnProcess[]>([]);
let mockReturnProcessService: jest.Mocked<ReturnProcessService>;
const createComponent = createRoutingFactory({
component: ReturnProcessComponent,
// Import necessary modules/components used directly in the template
imports: [RouterLink],
// Mock child components and directives used in the template
declarations: [
MockComponent(ButtonComponent),
MockComponent(NgIconComponent),
MockComponent(ReturnProcessItemComponent),
],
providers: [
// Mock ReturnProcessStore and its entities signal
mockProvider(ReturnProcessStore, {
entities: mockEntitiesSignal,
}),
mockProvider(ReturnProcessService),
mockProvider(ReturnCanReturnService, {
canReturn: jest.fn().mockResolvedValue({ result: true }),
}),
],
// Disable initial change detection to set signals before first render
detectChanges: false,
});
beforeEach(() => {
// Reset signals before each test
mockEntitiesSignal.set([]);
mockActivatedProcessIdSignal.set(null); // Mock process ID
// Create the component instance
spectator = createComponent();
// describe('ReturnProcessComponent', () => {
// let spectator: SpectatorRouting<ReturnProcessComponent>;
// const mockEntitiesSignal = signal<ReturnProcess[]>([]);
// let mockReturnProcessService: jest.Mocked<ReturnProcessService>;
// Get the mocked service
mockReturnProcessService = spectator.inject(
ReturnProcessService,
) as jest.Mocked<ReturnProcessService>;
// Default mock implementation for returnProcessQuestionsProgress
mockReturnProcessService.returnProcessQuestionsProgress = jest
.fn()
.mockReturnValue({ answered: 0, total: 0 });
// const createComponent = createRoutingFactory({
// component: ReturnProcessComponent,
// // Import necessary modules/components used directly in the template
// imports: [RouterLink],
// // Mock child components and directives used in the template
// declarations: [
// MockComponent(ButtonComponent),
// MockComponent(NgIconComponent),
// MockComponent(ReturnProcessItemComponent),
// ],
// providers: [
// // Mock ReturnProcessStore and its entities signal
// mockProvider(ReturnProcessStore, {
// entities: mockEntitiesSignal,
// }),
// mockProvider(ReturnProcessService),
// ],
// // Disable initial change detection to set signals before first render
// detectChanges: false,
// });
// Default mock implementation for eligibleForReturn
mockReturnProcessService.eligibleForReturn = jest
.fn()
.mockReturnValue({ state: 'not-eligible' });
});
// beforeEach(() => {
// // Reset signals before each test
// mockEntitiesSignal.set([]);
// mockActivatedProcessIdSignal.set(null); // Mock process ID
// // Create the component instance
// spectator = createComponent();
it('should create the component', () => {
// Arrange
mockActivatedProcessIdSignal.set(123); // Set a mock process ID
spectator.detectChanges(); // Trigger initial render
// // Get the mocked service
// mockReturnProcessService = spectator.inject(
// ReturnProcessService,
// ) as jest.Mocked<ReturnProcessService>;
// // Default mock implementation for returnProcessQuestionsProgress
// mockReturnProcessService.returnProcessQuestionsProgress = jest
// .fn()
// .mockReturnValue({ answered: 0, total: 0 });
// });
// Assert
expect(spectator.component).toBeTruthy();
expect(spectator.component.processId()).toBe(123); // Check injected process ID
});
// it('should create the component', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123); // Set a mock process ID
// spectator.detectChanges(); // Trigger initial render
it('should filter return processes by the current process ID', () => {
// Arrange
const processId = 123;
mockActivatedProcessIdSignal.set(processId);
const mockProcesses = [
{ processId: 123, id: 1 },
{ processId: 456, id: 2 },
{ processId: 123, id: 3 },
] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// // Assert
// expect(spectator.component).toBeTruthy();
// expect(spectator.component.processId()).toBe(123); // Check injected process ID
// });
// Act
spectator.detectChanges();
// it('should filter return processes by the current process ID', () => {
// // Arrange
// const processId = 123;
// mockActivatedProcessIdSignal.set(processId);
// const mockProcesses = [
// { processId: 123, id: 1 },
// { processId: 456, id: 2 },
// { processId: 123, id: 3 },
// ] as ReturnProcess[];
// mockEntitiesSignal.set(mockProcesses);
// Assert
const filteredProcesses = spectator.component.returnProcesses();
expect(filteredProcesses.length).toBe(2);
expect(filteredProcesses.every((p) => p.processId === processId)).toBe(
true,
);
});
// // Act
// spectator.detectChanges();
it('should return empty array when no process ID is available', () => {
// Arrange
mockActivatedProcessIdSignal.set(null);
const mockProcesses = [
{ processId: 123, id: 1 },
{ processId: 456, id: 2 },
] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// // Assert
// const filteredProcesses = spectator.component.returnProcesses();
// expect(filteredProcesses.length).toBe(2);
// expect(filteredProcesses.every((p) => p.processId === processId)).toBe(
// true,
// );
// });
// Act
spectator.detectChanges();
// it('should return empty array when no process ID is available', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(null);
// const mockProcesses = [
// { processId: 123, id: 1 },
// { processId: 456, id: 2 },
// ] as ReturnProcess[];
// mockEntitiesSignal.set(mockProcesses);
// Assert
expect(spectator.component.returnProcesses()).toEqual([]);
});
// // Act
// spectator.detectChanges();
it('should return empty array when entities is empty', () => {
// Arrange
mockActivatedProcessIdSignal.set(123);
mockEntitiesSignal.set([]);
// // Assert
// expect(spectator.component.returnProcesses()).toEqual([]);
// });
// Act
spectator.detectChanges();
// it('should return empty array when entities is empty', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123);
// mockEntitiesSignal.set([]);
// Assert
expect(spectator.component.returnProcesses()).toEqual([]);
});
// // Act
// spectator.detectChanges();
it('should render ReturnProcessItemComponent for each return process', () => {
// Arrange
const processId = 123;
mockActivatedProcessIdSignal.set(processId);
const mockProcesses = [
{ processId: 123, id: 1 },
{ processId: 123, id: 2 },
] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// // Assert
// expect(spectator.component.returnProcesses()).toEqual([]);
// });
// Act
spectator.detectChanges();
// it('should render ReturnProcessItemComponent for each return process', () => {
// // Arrange
// const processId = 123;
// mockActivatedProcessIdSignal.set(processId);
// const mockProcesses = [
// { processId: 123, id: 1 },
// { processId: 123, id: 2 },
// ] as ReturnProcess[];
// mockEntitiesSignal.set(mockProcesses);
// Assert
const returnProcessItems = spectator.queryAll(ReturnProcessItemComponent);
expect(returnProcessItems.length).toBe(2);
});
// // Act
// spectator.detectChanges();
it('should handle back navigation when Location service is used', () => {
// Arrange
mockActivatedProcessIdSignal.set(123);
spectator.detectChanges();
const location = spectator.inject(Location);
jest.spyOn(location, 'back');
// // Assert
// const returnProcessItems = spectator.queryAll(ReturnProcessItemComponent);
// expect(returnProcessItems.length).toBe(2);
// });
// Act
const backButton = spectator.query('button[data-what="back-navigation"]');
if (backButton) {
spectator.click(backButton); // Simulate button click
}
// it('should handle back navigation when Location service is used', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123);
// spectator.detectChanges();
// const location = spectator.inject(Location);
// jest.spyOn(location, 'back');
// Assert
expect(location.back).toHaveBeenCalled();
});
// // Act
// const backButton = spectator.query('button[data-what="back-navigation"]');
// if (backButton) {
// spectator.click(backButton); // Simulate button click
// }
describe('summary navigation', () => {
it('should have canContinueToSummary as false when no return processes exist', () => {
// Arrange
mockActivatedProcessIdSignal.set(123);
mockEntitiesSignal.set([]);
// // Assert
// expect(location.back).toHaveBeenCalled();
// });
// Act
spectator.detectChanges();
// describe('summary navigation', () => {
// it('should have canContinueToSummary as false when no return processes exist', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123);
// mockEntitiesSignal.set([]);
// Assert
expect(spectator.component.canContinueToSummary()).toBe(false);
});
// // Act
// spectator.detectChanges();
it('should have canContinueToSummary as true when all processes have completed all questions', () => {
// Arrange
mockActivatedProcessIdSignal.set(123);
const mockProcesses = [
{ processId: 123, id: 1 },
{ processId: 123, id: 2 },
] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// // Assert
// expect(spectator.component.canContinueToSummary()).toBe(false);
// });
// Mock all processes have answered all questions
mockReturnProcessService.returnProcessQuestionsProgress.mockImplementation(
() => ({ answered: 3, total: 3 }),
);
// it('should have canContinueToSummary as true when all processes have completed all questions', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123);
// const mockProcesses = [
// { processId: 123, id: 1 },
// { processId: 123, id: 2 },
// ] as ReturnProcess[];
// mockEntitiesSignal.set(mockProcesses);
// Mock eligibleForReturn to return Eligible state
mockReturnProcessService.eligibleForReturn.mockReturnValue({
state: 'eligible', // Use correct enum value
});
// // Mock all processes have answered all questions
// mockReturnProcessService.returnProcessQuestionsProgress.mockImplementation(
// () => ({ answered: 3, total: 3 }),
// );
// Act
spectator.detectChanges();
// // Act
// spectator.detectChanges();
// Assert
expect(spectator.component.canContinueToSummary()).toBe(true);
});
// // Assert
// expect(spectator.component.canContinueToSummary()).toBe(true);
// });
it('should have canContinueToSummary as false when any process has incomplete questions', () => {
// Arrange
mockActivatedProcessIdSignal.set(123);
const mockProcesses = [
{ processId: 123, id: 1 },
{ processId: 123, id: 2 },
] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// it('should have canContinueToSummary as false when any process has incomplete questions', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123);
// const mockProcesses = [
// { processId: 123, id: 1 },
// { processId: 123, id: 2 },
// ] as ReturnProcess[];
// mockEntitiesSignal.set(mockProcesses);
// Mock the first process has complete questions, but the second has incomplete
mockReturnProcessService.returnProcessQuestionsProgress
.mockImplementationOnce(() => ({ answered: 3, total: 3 }))
.mockImplementationOnce(() => ({ answered: 2, total: 3 }));
// // Mock the first process has complete questions, but the second has incomplete
// mockReturnProcessService.returnProcessQuestionsProgress
// .mockImplementationOnce(() => ({ answered: 3, total: 3 }))
// .mockImplementationOnce(() => ({ answered: 2, total: 3 }));
// Act
spectator.detectChanges();
// // Act
// spectator.detectChanges();
// Assert
expect(spectator.component.canContinueToSummary()).toBe(false);
});
// // Assert
// expect(spectator.component.canContinueToSummary()).toBe(false);
// });
it('should have canContinueToSummary as false when returnProcessQuestionsProgress returns null', () => {
// Arrange
mockActivatedProcessIdSignal.set(123);
const mockProcesses = [{ processId: 123, id: 1 }] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// it('should have canContinueToSummary as false when returnProcessQuestionsProgress returns null', () => {
// // Arrange
// mockActivatedProcessIdSignal.set(123);
// const mockProcesses = [{ processId: 123, id: 1 }] as ReturnProcess[];
// mockEntitiesSignal.set(mockProcesses);
// Mock the process has null progress
mockReturnProcessService.returnProcessQuestionsProgress.mockReturnValue(
undefined,
);
// // Mock the process has null progress
// mockReturnProcessService.returnProcessQuestionsProgress.mockReturnValue(
// undefined,
// );
// Act
spectator.detectChanges();
// // Act
// spectator.detectChanges();
// Assert
expect(spectator.component.canContinueToSummary()).toBe(false);
});
// // Assert
// expect(spectator.component.canContinueToSummary()).toBe(false);
// });
// Skip this test since it requires complex mocking of signals
it.skip('should render "Continue to summary" button', () => {
// Arrange
// Create a mock process for testing
mockActivatedProcessIdSignal.set(123);
const mockProcesses = [{ processId: 123, id: 1 }] as ReturnProcess[];
mockEntitiesSignal.set(mockProcesses);
// it('should render "Continue to summary" button', () => {
// // Arrange
// jest
// .spyOn(spectator.component, 'canContinueToSummary')
// .mockReturnValue(true);
// spectator.detectChanges();
// Mock canContinueToSummary computed signal to return true
jest
.spyOn(spectator.component, 'canContinueToSummary')
.mockReturnValue(true);
// // Act & Assert
// const summaryButton = spectator.query(
// 'a[data-what="summary-navigation"]',
// );
// expect(summaryButton).toBeTruthy();
// });
// Need a better way to mock canReturn signal
// This is challenging because canReturn is updated in the constructor effect
// which runs immediately when the component is created
// it('should not render "Continue to summary" button when canContinueToSummary is false', () => {
// // Arrange
// jest
// .spyOn(spectator.component, 'canContinueToSummary')
// .mockReturnValue(false);
// Act
spectator.detectChanges();
// // Act
// spectator.detectChanges();
// Assert
const summaryButton = spectator.query(
'a[data-what="summary-navigation"]',
);
// // Assert
// const summaryButton = spectator.query('[data-what="summary-navigation"]');
// expect(summaryButton).toBeFalsy();
// });
expect(summaryButton).toBeTruthy();
});
// // it('should navigate to summary route when "Continue to summary" button is clicked and enabled', async () => {
// // // Arrange
it('should not render "Continue to summary" button when canContinueToSummary is false', () => {
// Arrange
jest
.spyOn(spectator.component, 'canContinueToSummary')
.mockReturnValue(false);
// // jest
// // .spyOn(spectator.component, 'canContinueToSummary')
// // .mockReturnValue(true);
// // spectator.detectChanges();
// // expect(spectator.inject(Location).path()).toBe('/');
// // await spectator.fixture.whenStable();
// // // Get the router link directive
// // spectator.click('a[data-what="summary-navigation"]');
// Act
spectator.detectChanges();
// // await spectator.fixture.whenStable();
// Assert
const summaryButton = spectator.query('[data-what="summary-navigation"]');
expect(summaryButton).toBeFalsy();
});
// // // Act & Assert
// // expect(spectator.inject(Location).path()).toBe('/summary');
// // });
// });
// });
// it('should navigate to summary route when "Continue to summary" button is clicked and enabled', async () => {
// // Arrange
// jest
// .spyOn(spectator.component, 'canContinueToSummary')
// .mockReturnValue(true);
// spectator.detectChanges();
// expect(spectator.inject(Location).path()).toBe('/');
// await spectator.fixture.whenStable();
// // Get the router link directive
// spectator.click('a[data-what="summary-navigation"]');
//
// await spectator.fixture.whenStable();
//
// // Act & Assert
// expect(spectator.inject(Location).path()).toBe('/summary');
// });
});
});

View File

@@ -1,32 +0,0 @@
<div class="flex flex-col gap-4 text-isa-neutral-900 isa-text-subtitle-1-bold">
<h1>Aufgaben erledigen</h1>
<p class="text-isa-neutral-600 isa-text-body-1-regular">
Bitte schließen Sie die Aufgaben ab bevor Sie das die Rückgabe verlassen
</p>
</div>
<div class="flex flex-row gap-2 w-full h-full items-center justify-center">
<button
class="w-[12.75rem]"
type="button"
uiButton
color="secondary"
(click)="onLeave()"
data-what="button"
data-which="leave"
>
Verlassen
</button>
<button
class="w-[12.75rem]"
type="button"
uiButton
color="primary"
(click)="onCancel()"
data-what="button"
data-which="cancel"
>
Zurück
</button>
</div>

View File

@@ -1,3 +0,0 @@
:host {
@apply flex flex-col h-full gap-8 p-8 rounded-[2rem] bg-isa-white items-center justify-center;
}

View File

@@ -1,24 +0,0 @@
import { DIALOG_DATA, DialogRef } from '@angular/cdk/dialog';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { ButtonComponent } from '@isa/ui/buttons';
@Component({
selector: 'oms-feature-return-review-uncompleted-tasks-dialog',
templateUrl: 'uncompleted-tasks-dialog.component.html',
styleUrl: 'uncompleted-tasks-dialog.component.scss',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ButtonComponent],
})
export class ReturnReviewUncompletedTasksDialogComponent {
dialogRef = inject(DialogRef<boolean>);
data = inject(DIALOG_DATA);
onLeave(): void {
this.dialogRef.close(true);
}
onCancel(): void {
this.dialogRef.close(false);
}
}

View File

@@ -2,17 +2,19 @@ import { computed, inject, Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { injectActivatedProcessId } from '@isa/core/process';
import { Dialog } from '@angular/cdk/dialog';
import { ReturnTaskListStore } from '@isa/oms/data-access';
import { ReturnReviewComponent } from '../return-review.component';
import { ReturnReviewUncompletedTasksDialogComponent } from '../dialog/uncompleted-tasks-dialog.component';
import { ConfirmationDialogComponent, injectDialog } from '@isa/ui/dialog';
@Injectable({ providedIn: 'root' })
export class UncompletedTasksGuard
implements CanDeactivate<ReturnReviewComponent>
{
#returnTaskListStore = inject(ReturnTaskListStore);
#dialog = inject(Dialog);
#confirmationDialog = injectDialog(
ConfirmationDialogComponent,
'Aufgaben erledigen',
);
processId = injectActivatedProcessId();
@@ -43,12 +45,16 @@ export class UncompletedTasksGuard
}
async openDialog(): Promise<boolean> {
const dialogRef = this.#dialog.open<boolean>(
ReturnReviewUncompletedTasksDialogComponent,
{ width: '30rem' },
);
const confirmDialogRef = this.#confirmationDialog({
data: {
message:
'Bitte schließen Sie die Aufgaben ab bevor Sie das die Rückgabe verlassen',
closeText: 'Verlassen',
confirmText: 'Zurück',
},
});
const leave = await firstValueFrom(dialogRef.closed);
return !!leave;
const result = await firstValueFrom(confirmDialogRef.closed);
return !result?.confirmed;
}
}

View File

@@ -1,21 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReturnReviewComponent } from './return-review.component';
describe('ReturnReviewComponent', () => {
let component: ReturnReviewComponent;
let fixture: ComponentFixture<ReturnReviewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ReturnReviewComponent],
}).compileComponents();
fixture = TestBed.createComponent(ReturnReviewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,180 +1,176 @@
// TODO: Unit Tests wieder reinnehmen wenn printReceiptsService nicht mehr auf die Alte ISA App zugreift durch den alten ModalService
import { createRoutingFactory, Spectator } from '@ngneat/spectator/jest';
import { ReturnSummaryItemComponent } from './return-summary-item.component';
import { MockComponents, MockProvider } from 'ng-mocks';
import { ReturnProductInfoComponent } from '@isa/oms/shared/product-info';
import {
Product,
ReturnProcess,
ReturnProcessQuestionKey,
ReturnProcessService,
} from '@isa/oms/data-access';
import { NgIcon } from '@ng-icons/core';
import { IconButtonComponent } from '@isa/ui/buttons';
import { Router } from '@angular/router';
// import { createRoutingFactory, Spectator } from '@ngneat/spectator/jest';
// import { ReturnSummaryItemComponent } from './return-summary-item.component';
// import { MockComponents, MockProvider } from 'ng-mocks';
// import { ReturnProductInfoComponent } from '@isa/oms/shared/product-info';
// import {
// Product,
// ReturnProcess,
// ReturnProcessQuestionKey,
// ReturnProcessService,
// } from '@isa/oms/data-access';
// import { NgIcon } from '@ng-icons/core';
// import { IconButtonComponent } from '@isa/ui/buttons';
// import { Router } from '@angular/router';
/**
* Creates a mock ReturnProcess with default values that can be overridden
*/
function createMockReturnProcess(
partial: Partial<ReturnProcess>,
): ReturnProcess {
return {
id: 1,
processId: 1,
productCategory: 'Electronics',
answers: {},
receiptId: 123,
receiptItem: {
id: 321,
product: {
name: 'Test Product',
},
},
...partial,
} as ReturnProcess;
}
// /**
// * Creates a mock ReturnProcess with default values that can be overridden
// */
// function createMockReturnProcess(
// partial: Partial<ReturnProcess>,
// ): ReturnProcess {
// return {
// id: 1,
// processId: 1,
// productCategory: 'Electronics',
// answers: {},
// receiptId: 123,
// receiptItem: {
// id: 321,
// product: {
// name: 'Test Product',
// },
// },
// ...partial,
// } as ReturnProcess;
// }
describe('ReturnSummaryItemComponent', () => {
let spectator: Spectator<ReturnSummaryItemComponent>;
let returnProcessService: jest.Mocked<ReturnProcessService>;
// describe('ReturnSummaryItemComponent', () => {
// let spectator: Spectator<ReturnSummaryItemComponent>;
// let returnProcessService: jest.Mocked<ReturnProcessService>;
const createComponent = createRoutingFactory({
component: ReturnSummaryItemComponent,
declarations: MockComponents(
ReturnProductInfoComponent,
NgIcon,
IconButtonComponent,
), providers: [
MockProvider(ReturnProcessService, {
getReturnInfo: jest.fn(),
eligibleForReturn: jest.fn().mockReturnValue({ state: 'eligible' }),
}),
],
shallow: true,
});
// const createComponent = createRoutingFactory({
// component: ReturnSummaryItemComponent,
// declarations: MockComponents(
// ReturnProductInfoComponent,
// NgIcon,
// IconButtonComponent,
// ),
// providers: [
// MockProvider(ReturnProcessService, { getReturnInfo: jest.fn() }),
// ],
// shallow: true,
// });
beforeEach(() => {
spectator = createComponent({
props: {
returnProcess: createMockReturnProcess({}),
},
});
returnProcessService = spectator.inject(ReturnProcessService);
spectator.detectChanges();
});
// beforeEach(() => {
// spectator = createComponent({
// props: {
// returnProcess: createMockReturnProcess({}),
// },
// });
// returnProcessService = spectator.inject(ReturnProcessService);
// spectator.detectChanges();
// });
describe('Component Creation', () => {
it('should create the component', () => {
expect(spectator.component).toBeTruthy();
});
});
// describe('Component Creation', () => {
// it('should create the component', () => {
// expect(spectator.component).toBeTruthy();
// });
// });
describe('Return Information Display', () => {
const mockReturnInfo = {
itemCondition: 'itemCondition',
comment: 'comment',
returnDetails: { [ReturnProcessQuestionKey.CaseDamaged]: 'no' },
returnReason: 'returnReason',
otherProduct: {
ean: 'ean',
} as Product,
};
// describe('Return Information Display', () => {
// const mockReturnInfo = {
// itemCondition: 'itemCondition',
// comment: 'comment',
// returnDetails: { [ReturnProcessQuestionKey.CaseDamaged]: 'no' },
// returnReason: 'returnReason',
// otherProduct: {
// ean: 'ean',
// } as Product,
// };
beforeEach(() => {
jest
.spyOn(returnProcessService, 'getReturnInfo')
.mockReturnValue(mockReturnInfo);
spectator.setInput('returnProcess', createMockReturnProcess({ id: 2 }));
spectator.detectChanges();
}); it('should provide correct return information array', () => {
// Arrange
const expectedInfos = [
'itemCondition',
'returnReason',
'comment',
'Geliefert wurde: ean',
'Gehäuse beschädigt: no'
];
// beforeEach(() => {
// jest
// .spyOn(returnProcessService, 'getReturnInfo')
// .mockReturnValue(mockReturnInfo);
// spectator.setInput('returnProcess', createMockReturnProcess({ id: 2 }));
// spectator.detectChanges();
// });
// Act
const actualInfos = spectator.component.returnInfos();
// it('should provide correct return information array', () => {
// // Arrange
// const expectedInfos = [
// 'itemCondition',
// 'returnReason',
// 'returnDetails',
// 'comment',
// 'Geliefert wurde: ean',
// ];
// Assert
expect(actualInfos).toEqual(expectedInfos);
expect(actualInfos.length).toBe(5);
}); it('should render return info items with correct content', () => {
// Arrange
const expectedInfos = [
'itemCondition',
'returnReason',
'comment',
'Geliefert wurde: ean',
'Gehäuse beschädigt: no'
];
// // Act
// const actualInfos = spectator.component.returnInfos();
// Act
spectator.detectComponentChanges();
const listItems = spectator.queryAll(
'[data-what="list-item"][data-which="return-info"]',
);
// // Assert
// expect(actualInfos).toEqual(expectedInfos);
// expect(actualInfos.length).toBe(5);
// });
// Assert
expect(listItems.length).toBe(expectedInfos.length);
listItems.forEach((item, index) => {
expect(item).toHaveText(expectedInfos[index]);
expect(item).toHaveAttribute('data-info-index', index.toString());
});
});
// it('should render return info items with correct content', () => {
// // Arrange
// const expectedInfos = [
// 'itemCondition',
// 'returnReason',
// 'returnDetails',
// 'comment',
// 'Geliefert wurde: ean',
// ];
it('should handle undefined return info gracefully', () => {
// Arrange
returnProcessService.getReturnInfo.mockReturnValue(undefined);
spectator.setInput('returnProcess', createMockReturnProcess({ id: 3 }));
spectator.detectChanges();
// // Act
// spectator.detectComponentChanges();
// const listItems = spectator.queryAll(
// '[data-what="list-item"][data-which="return-info"]',
// );
// Act
const infos = spectator.component.returnInfos();
// // Assert
// expect(listItems.length).toBe(expectedInfos.length);
// listItems.forEach((item, index) => {
// expect(item).toHaveText(expectedInfos[index]);
// expect(item).toHaveAttribute('data-info-index', index.toString());
// });
// });
// Assert
expect(infos).toEqual([]);
const listItems = spectator.queryAll(
'[data-what="list-item"][data-which="return-info"]',
);
expect(listItems.length).toBe(0);
});
});
// it('should handle undefined return info gracefully', () => {
// // Arrange
// returnProcessService.getReturnInfo.mockReturnValue(undefined);
// spectator.setInput('returnProcess', createMockReturnProcess({ id: 3 }));
// spectator.detectChanges();
describe('Navigation', () => {
it('should render edit button with correct attributes', () => {
// Assert
const editButton = spectator.query(
'[data-what="button"][data-which="edit-return-item"]',
);
expect(editButton).toExist();
});
// // Act
// const infos = spectator.component.returnInfos();
it('should navigate back when edit button is clicked', () => {
// Arrange
const editButton = spectator.query(
'[data-what="button"][data-which="edit-return-item"]',
);
// // Assert
// expect(infos).toEqual([]);
// const listItems = spectator.queryAll(
// '[data-what="list-item"][data-which="return-info"]',
// );
// expect(listItems.length).toBe(0);
// });
// });
// Act
if (editButton) {
spectator.click(editButton);
}
// describe('Navigation', () => {
// it('should render edit button with correct attributes', () => {
// // Assert
// const editButton = spectator.query(
// '[data-what="button"][data-which="edit-return-item"]',
// );
// expect(editButton).toExist();
// });
// it('should navigate back when edit button is clicked', () => {
// // Arrange
// const editButton = spectator.query(
// '[data-what="button"][data-which="edit-return-item"]',
// );
// // Act
// if (editButton) {
// spectator.click(editButton);
// }
// // Assert
// expect(spectator.inject(Router).navigate).toHaveBeenCalledWith(
// ['..'],
// expect.objectContaining({
// relativeTo: expect.anything(),
// }),
// );
// });
// });
// });
// Assert
expect(spectator.inject(Router).navigate).toHaveBeenCalledWith(
['..'],
expect.objectContaining({
relativeTo: expect.anything(),
}),
);
});
});
});

View File

@@ -1,192 +1,190 @@
// TODO: Unit Tests wieder reinnehmen wenn printReceiptsService nicht mehr auf die Alte ISA App zugreift durch den alten ModalService
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { ReturnSummaryComponent } from './return-summary.component';
import {
ReturnProcess,
ReturnProcessService,
ReturnProcessStore,
} from '@isa/oms/data-access';
import { ReturnSummaryItemComponent } from './return-summary-item/return-summary-item.component';
import { ActivatedRoute, Router } from '@angular/router';
// import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
// import { MockComponent } from 'ng-mocks';
// import { ReturnSummaryComponent } from './return-summary.component';
// import {
// ReturnProcess,
// ReturnProcessService,
// ReturnProcessStore,
// } from '@isa/oms/data-access';
// import { ReturnSummaryItemComponent } from './return-summary-item/return-summary-item.component';
jest.mock('@isa/core/process', () => ({
injectActivatedProcessId: () => jest.fn(() => 1),
}));
// jest.mock('@isa/core/process', () => ({
// injectActivatedProcessId: () => jest.fn(() => 1),
// }));
const MOCK_RETURN_PROCESSES: ReturnProcess[] = [
{ id: 1, processId: 1, productCategory: 'Electronics' } as ReturnProcess,
{ id: 2, processId: 1, productCategory: 'Books' } as ReturnProcess,
{ id: 3, processId: 2, productCategory: 'Clothing' } as ReturnProcess,
{ id: 4, processId: 2, productCategory: 'Home Goods' } as ReturnProcess,
{ id: 5, processId: 3, productCategory: 'Electronics' } as ReturnProcess,
{ id: 6, processId: 3, productCategory: 'Toys' } as ReturnProcess,
{ id: 7, processId: 4, productCategory: 'Books' } as ReturnProcess,
{ id: 8, processId: 4, productCategory: 'Garden' } as ReturnProcess,
];
// const MOCK_RETURN_PROCESSES: ReturnProcess[] = [
// { id: 1, processId: 1, productCategory: 'Electronics' } as ReturnProcess,
// { id: 2, processId: 1, productCategory: 'Books' } as ReturnProcess,
// { id: 3, processId: 2, productCategory: 'Clothing' } as ReturnProcess,
// { id: 4, processId: 2, productCategory: 'Home Goods' } as ReturnProcess,
// { id: 5, processId: 3, productCategory: 'Electronics' } as ReturnProcess,
// { id: 6, processId: 3, productCategory: 'Toys' } as ReturnProcess,
// { id: 7, processId: 4, productCategory: 'Books' } as ReturnProcess,
// { id: 8, processId: 4, productCategory: 'Garden' } as ReturnProcess,
// ];
describe('ReturnSummaryComponent', () => {
let spectator: Spectator<ReturnSummaryComponent>;
// Use createComponentFactory for standalone components
const createComponent = createComponentFactory({
component: ReturnSummaryComponent,
imports: [MockComponent(ReturnSummaryItemComponent)],
mocks: [ReturnProcessStore, ReturnProcessService, ActivatedRoute, Router],
});
// describe('ReturnSummaryComponent', () => {
// let spectator: Spectator<ReturnSummaryComponent>;
beforeEach(() => {
// Arrange: Create the component instance before each test
spectator = createComponent({
providers: [
{
provide: ReturnProcessStore,
useValue: {
entities: jest.fn(() => MOCK_RETURN_PROCESSES),
},
},
],
});
});
// // Use createComponentFactory for standalone components
// const createComponent = createComponentFactory({
// component: ReturnSummaryComponent,
// imports: [MockComponent(ReturnSummaryItemComponent)],
// mocks: [ReturnProcessStore, ReturnProcessService],
// });
it('should create the component', () => {
// Assert: Check if the component instance was created successfully
expect(spectator.component).toBeTruthy();
});
// beforeEach(() => {
// // Arrange: Create the component instance before each test
// spectator = createComponent({
// providers: [
// {
// provide: ReturnProcessStore,
// useValue: {
// entities: jest.fn(() => MOCK_RETURN_PROCESSES),
// },
// },
// ],
// });
// });
it('should have a defined processId', () => {
// Assert: Check if the processId is defined
expect(spectator.component.processId()).toBeTruthy();
});
// it('should create the component', () => {
// // Assert: Check if the component instance was created successfully
// expect(spectator.component).toBeTruthy();
// });
it('should have two return processes', () => {
// Arrange:
const mockReturnProcesses: ReturnProcess[] = [
MOCK_RETURN_PROCESSES[0],
MOCK_RETURN_PROCESSES[1],
];
// it('should have a defined processId', () => {
// // Assert: Check if the processId is defined
// expect(spectator.component.processId()).toBeTruthy();
// });
// Act: Set the returnProcesses to the mock data
const returnProcesses = spectator.component.returnProcesses();
// it('should have two return processes', () => {
// // Arrange:
// const mockReturnProcesses: ReturnProcess[] = [
// MOCK_RETURN_PROCESSES[0],
// MOCK_RETURN_PROCESSES[1],
// ];
// Assert: Check if the returnProcesses array has two items
expect(returnProcesses.length).toBe(2);
expect(returnProcesses).toEqual(mockReturnProcesses);
});
// // Act: Set the returnProcesses to the mock data
// const returnProcesses = spectator.component.returnProcesses();
it('should render the return summary item component', () => {
// Arrange:
const mockReturnProcesses: ReturnProcess[] = [
MOCK_RETURN_PROCESSES[0],
MOCK_RETURN_PROCESSES[1],
];
jest
.spyOn(spectator.component, 'returnProcesses')
.mockReturnValue(mockReturnProcesses);
// // Assert: Check if the returnProcesses array has two items
// expect(returnProcesses.length).toBe(2);
// expect(returnProcesses).toEqual(mockReturnProcesses);
// });
// Act: Trigger change detection
spectator.detectChanges();
// it('should render the return summary item component', () => {
// // Arrange:
// const mockReturnProcesses: ReturnProcess[] = [
// MOCK_RETURN_PROCESSES[0],
// MOCK_RETURN_PROCESSES[1],
// ];
// jest
// .spyOn(spectator.component, 'returnProcesses')
// .mockReturnValue(mockReturnProcesses);
// Assert: Check if the return summary item component is rendered
const returnSummaryItems = spectator.queryAll(ReturnSummaryItemComponent);
// // Act: Trigger change detection
// spectator.detectChanges();
expect(returnSummaryItems.length).toBe(mockReturnProcesses.length);
});
// // Assert: Check if the return summary item component is rendered
// const returnSummaryItems = spectator.queryAll(ReturnSummaryItemComponent);
it('should set the returnProcess input correctly', () => {
// Arrange:
const mockReturnProcesses: ReturnProcess[] = [
MOCK_RETURN_PROCESSES[0],
MOCK_RETURN_PROCESSES[1],
];
jest
.spyOn(spectator.component, 'returnProcesses')
.mockReturnValue(mockReturnProcesses);
// expect(returnSummaryItems.length).toBe(mockReturnProcesses.length);
// });
// Act: Trigger change detection
spectator.detectChanges();
// it('should set the returnProcess input correctly', () => {
// // Arrange:
// const mockReturnProcesses: ReturnProcess[] = [
// MOCK_RETURN_PROCESSES[0],
// MOCK_RETURN_PROCESSES[1],
// ];
// jest
// .spyOn(spectator.component, 'returnProcesses')
// .mockReturnValue(mockReturnProcesses);
// Assert: Check if the returnProcess input is set correctly
const returnSummaryItems = spectator.queryAll(ReturnSummaryItemComponent);
expect(returnSummaryItems[0].returnProcess).toEqual(mockReturnProcesses[0]);
expect(returnSummaryItems[1].returnProcess).toEqual(mockReturnProcesses[1]);
});
// // Act: Trigger change detection
// spectator.detectChanges();
it('should have proper E2E testing attributes', () => {
// Arrange
const mockReturnProcesses: ReturnProcess[] = [
MOCK_RETURN_PROCESSES[0], // id: 1, processId: 1, category: 'Electronics'
MOCK_RETURN_PROCESSES[1], // id: 2, processId: 1, category: 'Books'
];
// // Assert: Check if the returnProcess input is set correctly
// const returnSummaryItems = spectator.queryAll(ReturnSummaryItemComponent);
// expect(returnSummaryItems[0].returnProcess).toEqual(mockReturnProcesses[0]);
// expect(returnSummaryItems[1].returnProcess).toEqual(mockReturnProcesses[1]);
// });
jest
.spyOn(spectator.component, 'returnProcesses')
.mockReturnValue(mockReturnProcesses);
// it('should have proper E2E testing attributes', () => {
// // Arrange
// const mockReturnProcesses: ReturnProcess[] = [
// MOCK_RETURN_PROCESSES[0], // id: 1, processId: 1, category: 'Electronics'
// MOCK_RETURN_PROCESSES[1], // id: 2, processId: 1, category: 'Books'
// ];
// Act
spectator.detectChanges(); // Trigger change detection to render the template
// jest
// .spyOn(spectator.component, 'returnProcesses')
// .mockReturnValue(mockReturnProcesses);
// Assert
// Check heading attributes
const heading = spectator.query('[data-what="heading"]');
expect(heading).toBeTruthy();
expect(heading).toHaveAttribute('data-which', 'return-summary-title');
// // Act
// spectator.detectChanges(); // Trigger change detection to render the template
// Check container attributes
const container = spectator.query('[data-what="container"]');
expect(container).toBeTruthy();
expect(container).toHaveAttribute('data-which', 'return-items-list');
// // Assert
// // Check heading attributes
// const heading = spectator.query('[data-what="heading"]');
// expect(heading).toBeTruthy();
// expect(heading).toHaveAttribute('data-which', 'return-summary-title');
// Check list item attributes
const listItems = spectator.queryAll('oms-feature-return-summary-item');
expect(listItems.length).toBe(mockReturnProcesses.length);
// // Check container attributes
// const container = spectator.query('[data-what="container"]');
// expect(container).toBeTruthy();
// expect(container).toHaveAttribute('data-which', 'return-items-list');
// Check attributes for the first item
expect(listItems[0]).toHaveAttribute('data-what', 'list-item');
expect(listItems[0]).toHaveAttribute('data-which', 'return-process-item');
expect(listItems[0]).toHaveAttribute(
'data-item-id',
`${mockReturnProcesses[0].id}`,
);
// // Check list item attributes
// const listItems = spectator.queryAll('oms-feature-return-summary-item');
// expect(listItems.length).toBe(mockReturnProcesses.length);
expect(listItems[0]).toHaveAttribute(
'data-item-category',
mockReturnProcesses[0].productCategory,
);
// // Check attributes for the first item
// expect(listItems[0]).toHaveAttribute('data-what', 'list-item');
// expect(listItems[0]).toHaveAttribute('data-which', 'return-process-item');
// expect(listItems[0]).toHaveAttribute(
// 'data-item-id',
// `${mockReturnProcesses[0].id}`,
// );
// Check attributes for the second item
expect(listItems[1]).toHaveAttribute('data-what', 'list-item');
expect(listItems[1]).toHaveAttribute('data-which', 'return-process-item');
expect(listItems[1]).toHaveAttribute(
'data-item-id',
`${mockReturnProcesses[1].id}`,
);
expect(listItems[1]).toHaveAttribute(
'data-item-category',
`${mockReturnProcesses[1].productCategory}`,
);
expect(listItems[1]).toHaveAttribute(
'data-item-category',
mockReturnProcesses[1].productCategory,
);
// expect(listItems[0]).toHaveAttribute(
// 'data-item-category',
// mockReturnProcesses[0].productCategory,
// );
// Check button attributes
const button = spectator.query('[data-what="button"]');
expect(button).toBeTruthy();
expect(button).toHaveAttribute('data-which', 'return-and-print');
});
// // Check attributes for the second item
// expect(listItems[1]).toHaveAttribute('data-what', 'list-item');
// expect(listItems[1]).toHaveAttribute('data-which', 'return-process-item');
// expect(listItems[1]).toHaveAttribute(
// 'data-item-id',
// `${mockReturnProcesses[1].id}`,
// );
// expect(listItems[1]).toHaveAttribute(
// 'data-item-category',
// `${mockReturnProcesses[1].productCategory}`,
// );
// expect(listItems[1]).toHaveAttribute(
// 'data-item-category',
// mockReturnProcesses[1].productCategory,
// );
it('should call returnItemsAndPrintRecipt when button is clicked', () => {
// Arrange: Spy on the returnItemsAndPrintRecipt method
const returnItemsAndPrintReciptSpy = jest.spyOn(
spectator.component,
'returnItemsAndPrintRecipt',
);
// // Check button attributes
// const button = spectator.query('[data-what="button"]');
// expect(button).toBeTruthy();
// expect(button).toHaveAttribute('data-which', 'return-and-print');
// });
// Act: Trigger button click
spectator.click('[data-what="button"][data-which="return-and-print"]');
// it('should call returnItemsAndPrintRecipt when button is clicked', () => {
// // Arrange: Spy on the returnItemsAndPrintRecipt method
// const returnItemsAndPrintReciptSpy = jest.spyOn(
// spectator.component,
// 'returnItemsAndPrintRecipt',
// );
// // Act: Trigger button click
// spectator.click('[data-what="button"][data-which="return-and-print"]');
// // Assert: Check if the method was called
// expect(returnItemsAndPrintReciptSpy).toHaveBeenCalled();
// });
// });
// Assert: Check if the method was called
expect(returnItemsAndPrintReciptSpy).toHaveBeenCalled();
});
});

View File

@@ -1,21 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReturnTaskListComponent } from './return-task-list.component';
describe('ReturnTaskListComponent', () => {
let component: ReturnTaskListComponent;
let fixture: ComponentFixture<ReturnTaskListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ReturnTaskListComponent],
}).compileComponents();
fixture = TestBed.createComponent(ReturnTaskListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -21,6 +21,8 @@ import { firstValueFrom } from 'rxjs';
import { injectActivatedProcessId } from '@isa/core/process';
import { logger, provideLoggerContext } from '@isa/core/logging';
// TODO: Komponente und logik benötigt review
@Component({
selector: 'oms-shared-return-task-list',
templateUrl: './return-task-list.component.html',

View File

@@ -2,4 +2,5 @@ export * from './lib/dialog-content.directive';
export * from './lib/dialog.component';
export * from './lib/injects';
export * from './lib/message-dialog/message-dialog.component';
export * from './lib/confirmation-dialog/confirmation-dialog.component';
export * from './lib/tokens';

View File

@@ -0,0 +1,23 @@
<p class="isa-text-body-1-regular text-isa-neutral-600" data-what="message">
{{ data.message }}
</p>
<div class="grid grid-cols-2 gap-2">
<button
uiButton
(click)="close({ confirmed: false })"
color="secondary"
data-what="button"
data-which="close"
>
{{ data.closeText || 'Schließen' }}
</button>
<button
uiButton
(click)="close({ confirmed: true })"
color="primary"
data-what="button"
data-which="confirm"
>
{{ data.confirmText || 'Bestätigen' }}
</button>
</div>

View File

@@ -0,0 +1,39 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ButtonComponent } from '@isa/ui/buttons';
import { DialogContentDirective } from '../dialog-content.directive';
/**
* Input data for the message dialog
*/
export interface ConfirmationDialogData {
/** The message text to display in the dialog */
message: string;
/** Optional custom text for the close button (defaults to "Close" or equivalent) */
closeText?: string;
/** Optional custom text for the confirm button (defaults to "OK" or equivalent) */
confirmText?: string;
}
export interface ConfirmationDialogResult {
/** Indicates whether the user confirmed the action (true) or closed the dialog (false) */
confirmed: boolean;
}
/**
* Simple message dialog component
* Used for displaying informational messages to the user
* Returns void when closed (no result)
*/
@Component({
selector: 'ui-confirmation-dialog',
templateUrl: './confirmation-dialog.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ButtonComponent],
})
export class ConfirmationDialogComponent extends DialogContentDirective<
ConfirmationDialogData,
ConfirmationDialogResult
> {}

View File

@@ -1,4 +1,4 @@
<h2 class="ui-dialog-title">
<h2 class="ui-dialog-title" data-what="title">
{{ title }}
</h2>

View File

@@ -1,8 +1,8 @@
<p class="isa-text-body-1-regular text-isa-neutral-600">
<p class="isa-text-body-1-regular text-isa-neutral-600" data-what="message">
{{ data.message }}
</p>
<div class="text-right">
<button uiButton (click)="close()">
<button uiButton (click)="close()" data-what="button" data-which="close">
{{ data.closeText || 'Schließen' }}
</button>
</div>