Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
| ![Statements](https://img.shields.io/badge/statements-6.38%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-1.42%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-4.45%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-6.07%25-red.svg?style=flat) |
| ![Statements](https://img.shields.io/badge/statements-11.53%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-10.61%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-8.5%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-10.75%25-red.svg?style=flat) |
Comment on lines 3 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean up repeated whitespaces in the table headers for better readability.

-| Statements                  | Branches                | Functions                 | Lines             |
+| Statements | Branches | Functions | Lines |

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
| ![Statements](https://img.shields.io/badge/statements-6.38%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-1.42%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-4.45%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-6.07%25-red.svg?style=flat) |
| ![Statements](https://img.shields.io/badge/statements-11.53%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-10.61%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-8.5%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-10.75%25-red.svg?style=flat) |
| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
| ![Statements](https://img.shields.io/badge/statements-11.53%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-10.61%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-8.5%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-10.75%25-red.svg?style=flat) |


Front-end repository for Certification Service integration

Expand Down
7 changes: 4 additions & 3 deletions src/pages/landing/components/ConnectSection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import { renderWithProviders } from 'utils/test-utils';
import ConnectSection from './ConnectSection';


describe('Landing page', () => {
describe('Landing page > Connection section', () => {

test('should render wallet connect button on landing page', () => {

renderWithProviders(<ConnectSection />);

const buttonElement = screen.getByRole('button', { name: /Connect your wallet/i });

expect(buttonElement).toBeVisible();

});
});

describe('Landing page modal', () => {
test('open modal when connect button is pressed and show 3 active wallets', async () => {

window.cardano = {
Expand Down Expand Up @@ -48,4 +48,5 @@ describe('Landing page modal', () => {
expect(screen.getByText('No wallet extensions installed yet!')).toBeVisible();

});

});
122 changes: 122 additions & 0 deletions src/pages/landing/components/RegisterModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

import { renderWithProviders } from 'utils/test-utils';
import RegisterModal from './RegisterModal';


describe('Landing page > Register modal', () => {

test('should not render register modal if show property is false', () => {

const props = {
show: false,
success: false,
transactionId: null,
onClose: () => {}
};

renderWithProviders(<RegisterModal {...props} />);

const dialogElement = screen.queryByTestId('register-modal');

expect(dialogElement).not.toBeInTheDocument();

});

test('should render register modal if show property is true', () => {

const props = {
show: true,
success: false,
transactionId: null,
onClose: () => {}
};

renderWithProviders(<RegisterModal {...props} />);

const dialogElement = screen.getByTestId('register-modal');

expect(dialogElement).toBeVisible();

});

test('should render register modal with success message if show and success properties are true', () => {

const props = {
show: true,
success: true,
transactionId: null,
onClose: () => {}
};

renderWithProviders(<RegisterModal {...props} />);

const successTitleElement = screen.queryByText('Successfully initiated subscription');

expect(successTitleElement).toBeVisible();

const loadingTitleElement = screen.queryByText('Setting up your subscription...');

expect(loadingTitleElement).not.toBeInTheDocument();

});

test('should render register modal with loading message if show property is true and success property is false', () => {

const props = {
show: true,
success: false,
transactionId: null,
onClose: () => {}
};

renderWithProviders(<RegisterModal {...props} />);

const loadingTitleElement = screen.queryByText('Setting up your subscription...');

expect(loadingTitleElement).toBeVisible();

const successTitleElement = screen.queryByText('Successfully initiated subscription');

expect(successTitleElement).not.toBeInTheDocument();

});

test('should render register modal with transaction link if show and success properties are true and transactionId is not null', () => {

const props = {
show: true,
success: true,
transactionId: '1234',
onClose: () => {}
};

renderWithProviders(<RegisterModal {...props} />);

const transactionElement = screen.queryByText('View your performed payment transaction');

expect(transactionElement).toBeVisible();

});

test('should render register modal and close after a click event on continue button', async () => {

const props = {
show: true,
success: true,
transactionId: null,
onClose: jest.fn()
};

renderWithProviders(<RegisterModal {...props} />);

await userEvent.click(screen.getByRole('button', { name: /Continue/i }));

expect(props.onClose.mock.calls).toHaveLength(1);

});

});
2 changes: 1 addition & 1 deletion src/pages/landing/components/RegisterModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface Props {

const RegisterModal = (props: Props) => {
return (
<Dialog open={props.show} onClose={props.onClose}>
<Dialog open={props.show} onClose={props.onClose} data-testid="register-modal">
<DialogTitle>
{props.success ? 'Successfully initiated subscription' : 'Setting up your subscription...'}
</DialogTitle>
Expand Down
107 changes: 107 additions & 0 deletions src/pages/landing/components/RegisterSection.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from 'react';
import { http, HttpResponse, delay } from 'msw';
import { setupServer } from 'msw/node';
import { screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

import { renderWithProviders } from 'utils/test-utils';
import RegisterSection from './RegisterSection';

import type { Tier } from "store/slices/tiers.slice";

const server = setupServer(
http.get('/ada-usd-price', async () => {
await delay(150);
return HttpResponse.json(10.0);
}),
);

const tier: Tier = {
id: '1',
name: 'Sample Tier',
subtitle: 'Sample Tier',
features: [],
usdPrice: 100,
enabled: true
};

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('Landing page > Register section', () => {

test('should render the register section', async () => {

renderWithProviders(<RegisterSection tier={tier} onSubmit={() => {}} />);

expect(screen.queryByText('Auditor profile')).toBeInTheDocument();

});

test('should render the subscription price correctly', async () => {

renderWithProviders(<RegisterSection tier={tier} onSubmit={() => {}} />);

const submitButton = await screen.findByText(/10.00/i);

expect(submitButton).toBeInTheDocument();

});

test('should render disabled form if the fetched price is zero', async () => {

setupServer(
http.get('/ada-usd-price', async () => {
await delay(150);
return HttpResponse.json(0.0);
}),
);

renderWithProviders(<RegisterSection tier={tier} onSubmit={() => {}} />);

const submitButton = await screen.findByText(/0.00/i);

expect(submitButton).toBeDisabled();

});

test('should submit the form successfully', async () => {

const onSubmit = jest.fn();

renderWithProviders(<RegisterSection tier={tier} onSubmit={onSubmit} />);

const submitButton = await screen.findByText(/10.00/i);

const companyNameInput = screen.getByRole('textbox', { name: 'Company name *' });
const contactEmailInput = screen.getByRole('textbox', { name: 'Contact email *' });
const companyEmailInput = screen.getByRole('textbox', { name: 'Company Email *' });
const fullNameInput = screen.getByRole('textbox', { name: 'Full name *' });
const twitterInput = screen.getByRole('textbox', { name: 'Twitter' });
const linkedinInput = screen.getByRole('textbox', { name: 'LinkedIn' });
const websiteInput = screen.getByRole('textbox', { name: 'Website' });

fireEvent.change(companyNameInput, { target: { value: 'Test company' } });
fireEvent.change(contactEmailInput, { target: { value: 'contact@test.com' } });
fireEvent.change(companyEmailInput, { target: { value: 'company@test.com' } });
fireEvent.change(fullNameInput, { target: { value: 'Full Name' } });
fireEvent.change(twitterInput, { target: { value: 'test' } });
fireEvent.change(linkedinInput, { target: { value: 'https://www.linkedin.com/profile/test' } });
fireEvent.change(websiteInput, { target: { value: 'https://www.test.com' } });

await userEvent.click(submitButton);

expect(onSubmit.mock.calls).toHaveLength(1);
expect(onSubmit.mock.calls[0][0].companyName).toBe('Test company');
expect(onSubmit.mock.calls[0][0].contactEmail).toBe('contact@test.com');
expect(onSubmit.mock.calls[0][0].email).toBe('company@test.com');
expect(onSubmit.mock.calls[0][0].fullName).toBe('Full Name');
expect(onSubmit.mock.calls[0][0].twitter).toBe('test');
expect(onSubmit.mock.calls[0][0].linkedin).toBe('https://www.linkedin.com/profile/test');
expect(onSubmit.mock.calls[0][0].website).toBe('https://www.test.com');

});

});
1 change: 0 additions & 1 deletion src/pages/landing/components/RegisterSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ const RegisterSection = (props: Props) => {
setCount(REFRESH_TIME);
dispatch(fetchPrice({}));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.tier]);

useEffect(() => {
Expand Down
28 changes: 16 additions & 12 deletions src/pages/landing/components/SubscriptionSection.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,30 @@ beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('fetches and render the subcriptions tiers', async () => {
describe('Landing page > Subscription section', () => {

renderWithProviders(<SubscriptionSection onSelectTier={() => {}} />);
test('fetches and render the subcriptions tiers', async () => {

expect(await screen.findByText(/Tier 1/i)).toBeInTheDocument();
expect(await screen.findByText(/Tier 2/i)).toBeInTheDocument();
renderWithProviders(<SubscriptionSection onSelectTier={() => {}} />);

});
expect(await screen.findByText(/Tier 1/i)).toBeInTheDocument();
expect(await screen.findByText(/Tier 2/i)).toBeInTheDocument();

test('render the subcriptions tiers and select one', async () => {
});

const onSelectTier = jest.fn();
test('render the subcriptions tiers and select one', async () => {

renderWithProviders(<SubscriptionSection onSelectTier={onSelectTier} />);
const onSelectTier = jest.fn();

await screen.findByText(/Tier 1/i);
renderWithProviders(<SubscriptionSection onSelectTier={onSelectTier} />);

await userEvent.click(screen.getAllByRole('button')[0]);
await screen.findByText(/Tier 1/i);

expect(onSelectTier.mock.calls).toHaveLength(1);
expect(onSelectTier.mock.calls[0][0].name).toBe('Tier 1');
await userEvent.click(screen.getAllByRole('button')[0]);

expect(onSelectTier.mock.calls).toHaveLength(1);
expect(onSelectTier.mock.calls[0][0].name).toBe('Tier 1');

});

});
3 changes: 1 addition & 2 deletions src/pages/landing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ConnectSection from "./components/ConnectSection";
import SubscriptionSection from "./components/SubscriptionSection";
import RegisterSection from "./components/RegisterSection";
import RegisterModal from "./components/RegisterModal";
import PaymentDetailsVerification from "components/PaymentConfirmation/PaymentDetailsVerification";

import { useAppDispatch, useAppSelector } from "store/store";
import { fetchActiveSubscription } from "store/slices/auth.slice";
Expand All @@ -14,8 +15,6 @@ import type { Tier } from "store/slices/tiers.slice";
import type { RegisterForm } from "store/slices/register.slice";

import "./index.css";
import { BigNum } from "@emurgo/cardano-serialization-lib-browser";
import PaymentDetailsVerification from "components/PaymentConfirmation/PaymentDetailsVerification";

export interface ISubscription {
adaUsdPrice: number;
Expand Down
7 changes: 3 additions & 4 deletions src/store/slices/walletConnection.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ const initialState: WalletConnectionState = {
// Add this export to your walletConnectionSlice file
export { initialState as walletConnectionInitialState };


const CardanoNS = window.cardano;

type StakeAddressHex = string;
type StakeAddressBech32 = `stake${string}`;
type ChangeAddressBech32 = `addr${string}`;
Expand All @@ -69,6 +66,8 @@ const getAddresses = async (wallet: any): Promise<[StakeAddressHex, StakeAddress
}

export const connectWallet = createAsyncThunk('connectWallet', async (payload: { walletName: string }, thunkApi) => {
const CardanoNS = window.cardano;

Comment on lines +69 to +70
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Review Summary

The review comment suggested that localizing the CardanoNS declaration within the connectWallet function in walletConnection.slice.ts was a good practice for encapsulation and reducing global namespace pollution. It also recommended verifying whether CardanoNS is used globally elsewhere in the codebase that might be affected by this change.

Verification Results

The executed script searched for occurrences of CardanoNS outside the walletConnection.slice.ts file. The results indicate that CardanoNS is indeed used in other parts of the codebase, specifically within ConnectSection.tsx. This file uses CardanoNS to check for installed wallet extensions and to display wallet-related information.

Conclusion

Given that CardanoNS is used in multiple parts of the application, the change to localize its declaration within connectWallet function might lead to issues if other parts of the application rely on a globally accessible CardanoNS. This could potentially break functionality in the ConnectSection.tsx where CardanoNS is expected to be globally accessible.

Action Required:
It is recommended to ensure that the changes in walletConnection.slice.ts do not inadvertently affect other components that rely on CardanoNS. A possible solution could be to maintain CardanoNS as a global variable or ensure that all components have access to it as needed.

Analysis chain

Localizing the CardanoNS declaration within the connectWallet function is a good practice for encapsulation and reducing global namespace pollution.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that `CardanoNS` is not used globally elsewhere in the codebase that might be affected by this change.
rg --type ts "CardanoNS" --glob '!src/store/slices/walletConnection.slice.ts'

Length of output: 762

try {
const { walletName } = payload;
const wallet = await CardanoNS[walletName].enable();
Expand Down Expand Up @@ -115,6 +114,7 @@ export const connectWallet = createAsyncThunk('connectWallet', async (payload: {
});

export const startListenWalletChanges = createAsyncThunk('listenWalletChanges', async (payload: any, { dispatch, getState }) => {
const CardanoNS = window.cardano;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Review Summary

The review comment approved the localization of the CardanoNS declaration within the startListenWalletChanges function in walletConnection.slice.ts, suggesting that it aligns with good practices for reducing global namespace pollution. The review also included a verification script to check if CardanoNS is used globally elsewhere in the codebase that might be affected by this change.

Verification Findings

The script output indicates that CardanoNS is indeed used in other parts of the codebase, specifically within ConnectSection.tsx. This file contains multiple references to CardanoNS, which is used to check for the presence of wallet extensions and to display wallet-related information. This suggests that CardanoNS is not exclusively local to walletConnection.slice.ts and is required globally.

Conclusion

Given the usage of CardanoNS in multiple components outside of walletConnection.slice.ts, the localization of CardanoNS within startListenWalletChanges might lead to issues if other parts of the application rely on a globally accessible CardanoNS. This could potentially break functionality in ConnectSection.tsx where CardanoNS is expected to be available globally.

Action Required:
The localization of CardanoNS should be reconsidered, or adjustments should be made to ensure that all components that require CardanoNS have access to it. It is recommended to either maintain CardanoNS as a global variable or ensure that it is passed appropriately to all components that depend on it.

Analysis chain

Localizing the CardanoNS declaration within the startListenWalletChanges function is consistent with good practices for reducing global namespace pollution.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that `CardanoNS` is not used globally elsewhere in the codebase that might be affected by this change.
rg --type ts "CardanoNS" --glob '!src/store/slices/walletConnection.slice.ts'

Length of output: 762

const { authToken, networkId } = (getState() as RootState).session;
const { wallet, walletName, stakeAddress } = (getState() as RootState).walletConnection;

Expand Down Expand Up @@ -151,7 +151,6 @@ export const startListenWalletChanges = createAsyncThunk('listenWalletChanges',
}
}
} catch (error) {
console.log(error);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
Expand Down
Loading