Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@ All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). The format is based on the `RELEASE-NOTES-TEMPLATE.md` file.

## Release 2.15.0

## Introduction
* Product name: Open Supply Hub
* Release date: November 1, 2025

### What's new
* [OSDEV-2200](https://opensupplyhub.atlassian.net/browse/OSDEV-2200) - Implements a new claim introduction page for the new facility claiming process, accessible via `/claim/:osId`, which can be enabled or activated through a feature flag.

### Release instructions
* Ensure that the following commands are included in the `post_deployment` command:
* `migrate`
* `reindex_database`


## Release 2.14.0

## Introduction
Expand Down
17 changes: 17 additions & 0 deletions src/react/src/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import SearchByOsIdResult from './components/Contribute/SearchByOsIdResult';
import SearchByNameAndAddressResult from './components/Contribute/SearchByNameAndAddressResult';
import ProductionLocationInfo from './components/Contribute/ProductionLocationInfo';
import withProductionLocationSubmit from './components/Contribute/HOC/withProductionLocationSubmit';
import ClaimIntro from './components/V1Claim/ClaimIntro';

import { sessionLogin } from './actions/auth';
import { fetchFeatureFlags } from './actions/featureFlags';
Expand All @@ -55,8 +56,10 @@ import {
facilitiesRoute,
dashboardRoute,
claimFacilityRoute,
claimIntroRoute,
claimedFacilitiesRoute,
CLAIM_A_FACILITY,
ENABLE_V1_CLAIMS_FLOW,
settingsRoute,
InfoLink,
InfoPaths,
Expand Down Expand Up @@ -106,6 +109,20 @@ class Routes extends Component {
id="mainPanel"
>
<Switch>
<Route
exact
path={claimIntroRoute}
render={() => (
<FeatureFlag
flag={ENABLE_V1_CLAIMS_FLOW}
alternative={
<Route component={Facilities} />
}
>
<Route component={ClaimIntro} />
</FeatureFlag>
)}
/>
<Route
exact
path={claimFacilityRoute}
Expand Down
190 changes: 190 additions & 0 deletions src/react/src/__tests__/components/ClaimInfoSection.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import React from 'react';
import { fireEvent, waitFor } from '@testing-library/react';
import renderWithProviders from '../../util/testUtils/renderWithProviders';
import ClaimInfoSection from '../../components/V1Claim/ClaimInfoSection';

describe('ClaimInfoSection component', () => {
const renderComponent = () => renderWithProviders(<ClaimInfoSection />);

test('renders without crashing', () => {
renderComponent();
});

describe('Step 1 - Eligibility rendering', () => {
test('displays Step 1 title', () => {
const { getByText } = renderComponent();

expect(getByText('Confirm Your Eligibility')).toBeInTheDocument();
});

test('displays eligibility requirements', () => {
const { getByText } = renderComponent();

expect(
getByText(/Claim requests must be submitted by a current employee/)
).toBeInTheDocument();
expect(
getByText(/If you're not an owner or manager/)
).toBeInTheDocument();
});
});

describe('Step 2 - Prove Your Name and Role rendering', () => {
test('displays Step 2 title', () => {
const { getByText } = renderComponent();

expect(getByText('Prove Your Name and Role')).toBeInTheDocument();
});

test('displays OPTIONS list', () => {
const { getByText } = renderComponent();

expect(
getByText(/Company website showing your name and role/)
).toBeInTheDocument();
expect(getByText(/Employee ID badge/)).toBeInTheDocument();
expect(getByText(/Employment letter/)).toBeInTheDocument();
});

test('displays employee examples for Step 2', () => {
const { getAllByAltText } = renderComponent();

expect(
getAllByAltText('Example employee ID badge')[0]
).toBeInTheDocument();
expect(
getAllByAltText('Example employment letter')[0]
).toBeInTheDocument();
expect(
getAllByAltText('Example business card')[0]
).toBeInTheDocument();
});
});

describe('Step 3 - Prove Your Company Name and Address rendering', () => {
test('displays Step 3 title', () => {
const { getByText } = renderComponent();

expect(
getByText('Prove Your Company Name and Address')
).toBeInTheDocument();
});

test('displays company verification OPTIONS', () => {
const { getByText } = renderComponent();

expect(getByText(/Business registration/)).toBeInTheDocument();
expect(getByText(/Business license/)).toBeInTheDocument();
expect(getByText(/Utility bill/)).toBeInTheDocument();
});

test('displays NOTE about company name and address', () => {
const { getByText } = renderComponent();

expect(
getByText(/The document must show the same company name and address/)
).toBeInTheDocument();
});

test('displays company examples for Step 3', () => {
const { getAllByAltText } = renderComponent();

expect(
getAllByAltText('Example business registration certificate')[0]
).toBeInTheDocument();
expect(
getAllByAltText('Example business license')[0]
).toBeInTheDocument();
expect(
getAllByAltText('Example utility bill')[0]
).toBeInTheDocument();
});
});

describe('Step 4 and 5 - Maximum Value section rendering', () => {
test('displays Maximum Value badge', () => {
const { getByText } = renderComponent();

expect(getByText('Maximum Value')).toBeInTheDocument();
});

test('displays Step 4 - Add Key Details', () => {
const { getByText } = renderComponent();

expect(getByText(/Add Key Details:/)).toBeInTheDocument();
expect(
getByText(/Provide information about the production location/)
).toBeInTheDocument();
});

test('displays Step 5 - Get Verified', () => {
const { getByText } = renderComponent();

expect(getByText(/Get Verified:/)).toBeInTheDocument();
expect(
getByText(/After the claim is approved, you get a credible/)
).toBeInTheDocument();
});
});

describe('Important Note rendering', () => {
test('displays important warning box', () => {
const { getByText } = renderComponent();

expect(getByText('IMPORTANT!')).toBeInTheDocument();
expect(
getByText(/Any documentation appearing to be forged/)
).toBeInTheDocument();
});

test('displays info icon in warning box', () => {
const { container } = renderComponent();

const svgIcons = container.querySelectorAll('svg');
expect(svgIcons.length).toBeGreaterThan(0);
});
});

describe('Image Dialog functionality', () => {
test('opens dialog when example image is clicked', async () => {
const { getAllByAltText, getByRole } = renderComponent();

const exampleImage = getAllByAltText('Example employee ID badge')[0];
const imageButton = exampleImage.closest('button');

fireEvent.click(imageButton);

await waitFor(() => {
const dialog = getByRole('dialog');
expect(dialog).toBeInTheDocument();
});
});

test('displays image in dialog when opened', async () => {
const { getAllByAltText } = renderComponent();

const exampleImage = getAllByAltText('Example employee ID badge')[0];
const imageButton = exampleImage.closest('button');

fireEvent.click(imageButton);

await waitFor(() => {
const dialogImages = getAllByAltText('Example employee ID badge');

expect(dialogImages.length).toBeGreaterThan(1);
});
});
});

describe('External link', () => {
test('displays learn more link', () => {
const { getByText } = renderComponent();

const link = getByText('Learn more about claiming your production location');
expect(link).toBeInTheDocument();
expect(link.tagName).toBe('A');
expect(link).toHaveAttribute('href', 'https://info.opensupplyhub.org/resources/claim-a-facility');
expect(link).toHaveAttribute('target', '_blank');
});
});
});
125 changes: 125 additions & 0 deletions src/react/src/__tests__/components/ClaimIntro.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from 'react';
import { Router } from 'react-router-dom';
import { fireEvent } from '@testing-library/react';
import history from '../../util/history';
import renderWithProviders from '../../util/testUtils/renderWithProviders';
import ClaimIntro from '../../components/V1Claim/ClaimIntro';
import { makeClaimDetailsLink } from '../../util/util';

jest.mock('../../components/V1Claim/ClaimInfoSection', () => () => (
<div data-testid="claim-info-section">ClaimInfoSection</div>
));

describe('ClaimIntro component', () => {
const mockOsID = 'TEST123';
const mockMatch = {
params: { osID: mockOsID },
};

const renderComponent = (userHasSignedIn = true) => {
const preloadedState = {
auth: {
user: {
user: {
isAnon: !userHasSignedIn,
},
},
},
};

return renderWithProviders(
<Router history={history}>
<ClaimIntro match={mockMatch} />
</Router>,
{ preloadedState }
);
};

beforeEach(() => {
jest.clearAllMocks();
history.push('/');
});

describe('Authentication checks', () => {
test('renders RequireAuthNotice when user is not signed in', () => {
const { getByText } = renderComponent(false);

expect(getByText('Claim this production location')).toBeInTheDocument();
expect(
getByText('Log in to claim a production location on Open Supply Hub')
).toBeInTheDocument();
});

test('renders main content when user is signed in', () => {
const { getByText, getByTestId } = renderComponent(true);

expect(getByText('Claim a Production Location')).toBeInTheDocument();
expect(getByTestId('claim-info-section')).toBeInTheDocument();
});
});

describe('Component rendering', () => {
test('renders without crashing', () => {
renderComponent();
});

test('displays the correct title', () => {
const { getByText } = renderComponent();

expect(getByText('Claim a Production Location')).toBeInTheDocument();
});

test('displays the subtitle text', () => {
const { getByText } = renderComponent();

expect(
getByText(/In order to submit a claim request/)
).toBeInTheDocument();
});

test('renders both action buttons', () => {
const { getByText } = renderComponent();

expect(getByText('GO BACK')).toBeInTheDocument();
expect(getByText('Continue to Claim Form')).toBeInTheDocument();
});

test('renders ClaimInfoSection component', () => {
const { getByTestId } = renderComponent();

expect(getByTestId('claim-info-section')).toBeInTheDocument();
});
});

describe('Navigation functionality', () => {
test('navigates back when GO BACK button is clicked', () => {
history.push('/some-page');
const previousPath = history.location.pathname;

const { getByText } = renderComponent();
const backButton = getByText('GO BACK');

fireEvent.click(backButton);

expect(history.location.pathname).toBe(previousPath);
});

test('navigates to claim details when Continue button is clicked', () => {
const { getByText } = renderComponent();
const continueButton = getByText('Continue to Claim Form');

fireEvent.click(continueButton);

const expectedPath = makeClaimDetailsLink(mockOsID);
expect(history.location.pathname).toBe(expectedPath);
});
});

describe('Props validation', () => {
test('receives osID from route params', () => {
const { container } = renderComponent();

expect(container.firstChild).toBeInTheDocument();
});
});
});
Loading