Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8c347e9
feat: updated rwa exmaple and added bootstrap script
mustafadeel Nov 28, 2025
1ab48bc
feat: updated readme file with boostrap instructions
mustafadeel Nov 28, 2025
7e42de3
Merge branch 'main' into example_update
mustafadeel Nov 28, 2025
3a725be
feat(examples): fix linter
castrinho18 Nov 29, 2025
7b034ce
test(react): added integration test cases for idp hooks (#203)
rax7389 Dec 1, 2025
21dfc94
feat(examples): readme feedback
mustafadeel Dec 1, 2025
0d29707
test: added tests for core schemas (#238)
Ajeey Dec 1, 2025
ba86168
Merge branch 'main' into example_update
mustafadeel Dec 1, 2025
2cc8074
Merge remote-tracking branch 'origin/main' into beta-testing
grandmaester Dec 2, 2025
ee82b58
test(react): add tests to idp hooks (domains tab) (#241)
rax7389 Dec 2, 2025
12d478a
chore: Added Tests for React Hooks folder (#243)
grandmaester Dec 2, 2025
b243fa8
feat(examples): add check for my-org api in bootstrap
mustafadeel Dec 2, 2025
1318999
Chore: Tests for IDP componnents (#242)
grandmaester Dec 3, 2025
3de0b61
chore: Added Tests for MFA Components folder (#244)
grandmaester Dec 3, 2025
517b31d
feat(examples): fixed prettier issues
mustafadeel Dec 3, 2025
9a7410d
feat(examples): Add my-account flag and align readmes (#247)
castrinho18 Dec 3, 2025
a384c9f
feat(examples) Update and align examples and readme files (#233)
mustafadeel Dec 4, 2025
18acd0e
fix(react): store file value instead of name (#246)
NaveenChand755 Dec 4, 2025
0a3c92b
feat(examples): bootstrap script updates
mustafadeel Dec 5, 2025
39bbcba
feat(examples): updated bootstrap script
mustafadeel Dec 5, 2025
fce6470
feat(examples): updated readme
mustafadeel Dec 5, 2025
cb6d557
Merge branch 'main' into beta-testing
Ajeey Dec 7, 2025
3b35169
Merge remote-tracking branch 'origin/beta-testing' into example_update
Ajeey Dec 7, 2025
06c03d2
chore: updated error msg if client creation fails
Ajeey Dec 7, 2025
fe085bb
fix(examples): update lock file
NaveenChand755 Dec 8, 2025
3c97b7c
Merge pull request #3 from mustafadeel/example_update
Ajeey Dec 8, 2025
bd4ba9a
fix(examples): lint error fix
NaveenChand755 Dec 8, 2025
3442077
chore: adding tests for sso provisioning
grandmaester Dec 8, 2025
4ff6f72
Merge remote-tracking branch 'origin/main' into chore/sso-provisionin…
grandmaester Dec 8, 2025
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
4 changes: 3 additions & 1 deletion examples/next-rwa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
"react-dom": "^19.2.1",
"react-hook-form": "^7.57.0",
"react-i18next": "^15.5.3",
"tailwindcss": "^4.1.17"
"tailwindcss": "^4.1.17",
"execa": "^9.1.0",
"ora": "^8.0.1"
},
"devDependencies": {
"@types/react": "^19.1.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import type { IdpScimTokenBase } from '@auth0/web-ui-components-core';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi, beforeEach } from 'vitest';

import { ProvisioningManageToken } from '../provisioning-manage-token';

// Mock hooks
vi.mock('../../../../../../hooks/use-translator', () => ({
useTranslator: () => ({
t: (key: string, params?: any) => {

Check warning on line 11 in packages/react/src/components/my-org/idp-management/sso-provider-edit/sso-provisioning/__tests__/provisioning-manage-token.test.tsx

View workflow job for this annotation

GitHub Actions / 🔄 Continuous Integration

Unexpected any. Specify a different type
if (key === 'delete_button_label') return 'Delete';
if (key === 'remove_button_label') return 'Remove';
if (key === 'title' && params?.providerName) return `Delete ${params.providerName}`;
return key;
},
}),
}));

describe('ProvisioningManageToken', () => {
const mockOnListScimTokens = vi.fn();
const mockOnCreateScimToken = vi.fn();
const mockOnDeleteScimToken = vi.fn();

const defaultProps = {
isScimTokensLoading: false,
isScimTokenCreating: false,
isScimTokenDeleting: false,
onListScimTokens: mockOnListScimTokens,
onCreateScimToken: mockOnCreateScimToken,
onDeleteScimToken: mockOnDeleteScimToken,
};

const mockTokens: IdpScimTokenBase[] = [
{
token_id: 'token-1',
valid_until: undefined,
created_at: '',
},
{
token_id: 'token-2',
valid_until: '2025-12-31T23:59:59Z',
created_at: '',
},
];

const expiredToken: IdpScimTokenBase = {
token_id: 'token-expired',
valid_until: '2020-01-01T00:00:00Z',
created_at: '',
};

beforeEach(() => {
vi.clearAllMocks();
mockOnListScimTokens.mockResolvedValue({ scim_tokens: [] });
});

it('should render title and description', async () => {
render(<ProvisioningManageToken {...defaultProps} />);

expect(await screen.findByText('title')).toBeInTheDocument();
expect(await screen.findByText('description')).toBeInTheDocument();
});

it('should render generate button', async () => {
render(<ProvisioningManageToken {...defaultProps} />);

expect(await screen.findByText('generate_button_label')).toBeInTheDocument();
});

it('should load tokens on mount', async () => {
mockOnListScimTokens.mockResolvedValue({ scim_tokens: mockTokens });
render(<ProvisioningManageToken {...defaultProps} />);

await waitFor(() => {
expect(mockOnListScimTokens).toHaveBeenCalled();
});
});

it('should show loading spinner when isScimTokensLoading is true', async () => {
render(<ProvisioningManageToken {...defaultProps} isScimTokensLoading={true} />);
expect(await screen.findByText('Loading...')).toBeInTheDocument();
});

it('should display tokens when loaded', async () => {
mockOnListScimTokens.mockResolvedValue({ scim_tokens: mockTokens });
render(<ProvisioningManageToken {...defaultProps} />);

expect(await screen.findByText(/token-1/)).toBeInTheDocument();
expect(await screen.findByText(/token-2/)).toBeInTheDocument();
});

it('should show active badge for active tokens', async () => {
mockOnListScimTokens.mockResolvedValue({ scim_tokens: mockTokens });
render(<ProvisioningManageToken {...defaultProps} />);

const badges = await screen.findAllByText('token_item.status_active');
expect(badges).toHaveLength(2);
});

it('should show expired badge for expired tokens', async () => {
mockOnListScimTokens.mockResolvedValue({ scim_tokens: [expiredToken] });
render(<ProvisioningManageToken {...defaultProps} />);

expect(await screen.findByText('token_item.status_expired')).toBeInTheDocument();
});

it('should show "never expire" text for tokens without valid_until', async () => {
mockOnListScimTokens.mockResolvedValue({
scim_tokens: [{ token_id: 'token-1', valid_until: undefined }],
});
render(<ProvisioningManageToken {...defaultProps} />);

expect(await screen.findByText('token_item.never_expire')).toBeInTheDocument();
});

it('should call onCreateScimToken when generate button is clicked', async () => {
const user = userEvent.setup();
const mockCreatedToken = {
scim_token: 'new-token-value',
token_id: 'new-token-id',
};
mockOnCreateScimToken.mockResolvedValue(mockCreatedToken);
mockOnListScimTokens.mockResolvedValue({ scim_tokens: [] });

render(<ProvisioningManageToken {...defaultProps} />);

const generateButton = await screen.findByText('generate_button_label');
await user.click(generateButton);

await waitFor(() => {
expect(mockOnCreateScimToken).toHaveBeenCalledWith({ token_lifetime: 3600 });
});
});

it('should disable generate button when max tokens reached', async () => {
mockOnListScimTokens.mockResolvedValue({ scim_tokens: mockTokens });
render(<ProvisioningManageToken {...defaultProps} />);

const generateButton = await screen.findByText('generate_button_label');
expect(generateButton).toBeDisabled();
});

it('should disable generate button when isScimTokenCreating is true', () => {
render(<ProvisioningManageToken {...defaultProps} isScimTokenCreating={true} />);

const generateButton = screen.getByText('generate_button_label');
expect(generateButton).toBeDisabled();
});

it('should show spinner on generate button when creating token', async () => {
render(<ProvisioningManageToken {...defaultProps} isScimTokenCreating={true} />);

const spinners = await screen.findAllByText('Loading...');
expect(spinners.length).toBeGreaterThan(0);
});

it('should open delete modal when delete button is clicked', async () => {
mockOnListScimTokens.mockResolvedValue({
scim_tokens: [{ token_id: 'token-1', valid_until: null }],
});
render(<ProvisioningManageToken {...defaultProps} />);

await screen.findByText(/token-1/);

const deleteButton = screen.findByRole('button', { name: /delete/i });
fireEvent.click(await deleteButton);
expect(await screen.findByRole('dialog')).toBeInTheDocument();
});

it('should call onDeleteScimToken when delete is confirmed', async () => {
const user = userEvent.setup();
mockOnListScimTokens.mockResolvedValue({
scim_tokens: [{ token_id: 'token-1', valid_until: null }],
});
render(<ProvisioningManageToken {...defaultProps} />);

await screen.findByText(/token-1/);

const deleteButton = screen.findByRole('button', { name: /delete/i });
fireEvent.click(await deleteButton);
await screen.findByRole('dialog');

const confirmButton = screen.findByText('delete_modal.delete_button_label');
user.click(await confirmButton);
await waitFor(() => {
expect(mockOnDeleteScimToken).toHaveBeenCalledWith('token-1');
});
});

it('should disable delete buttons when isScimTokenDeleting is true', async () => {
mockOnListScimTokens.mockResolvedValue({
scim_tokens: [{ token_id: 'token-1', valid_until: null }],
});
render(<ProvisioningManageToken {...defaultProps} isScimTokenDeleting={true} />);

await screen.findByText(/token-1/);

const deleteButtons = screen.getAllByRole('button', { name: /delete/i });
deleteButtons.forEach((btn) => {
expect(btn).toBeDisabled();
});
});

it('should reload tokens after successful creation', async () => {
const user = userEvent.setup();
const mockCreatedToken = {
scim_token: 'new-token-value',
token_id: 'new-token-id',
};
mockOnCreateScimToken.mockResolvedValue(mockCreatedToken);
mockOnListScimTokens.mockResolvedValueOnce({ scim_tokens: [] }).mockResolvedValueOnce({
scim_tokens: [{ token_id: 'new-token-id', valid_until: undefined }],
});

render(<ProvisioningManageToken {...defaultProps} />);

const generateButton = await screen.findByText('generate_button_label');
await user.click(generateButton);

await waitFor(() => {
expect(mockOnListScimTokens).toHaveBeenCalledTimes(2);
});
});

it('should apply custom styling classes', async () => {
const styling = {
variables: { common: {}, light: {}, dark: {} },
classes: {
'ProvisioningManageToken-root': 'custom-root-class',
'ProvisioningManageToken-card': 'custom-card-class',
},
};

const { container } = render(<ProvisioningManageToken {...defaultProps} styling={styling} />);

await waitFor(() => {
expect(container.querySelector('.custom-root-class')).toBeInTheDocument();
});
});

it('should not render card content when no tokens exist', async () => {
mockOnListScimTokens.mockResolvedValue({ scim_tokens: [] });
render(<ProvisioningManageToken {...defaultProps} />);

await waitFor(() => {
expect(screen.queryByText('token_item.token_prefix')).not.toBeInTheDocument();
});
});

it('should show create modal after generating token', async () => {
const user = userEvent.setup();
const mockCreatedToken = {
scim_token: 'new-token-value',
token_id: 'new-token-id',
};
mockOnCreateScimToken.mockResolvedValue(mockCreatedToken);
mockOnListScimTokens.mockResolvedValue({ scim_tokens: [] });

render(<ProvisioningManageToken {...defaultProps} />);

const generateButton = await screen.findByText('generate_button_label');
await user.click(generateButton);

expect(await screen.findByRole('dialog')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi, beforeEach } from 'vitest';

import { SsoProvisioningDeleteModal } from '../sso-provisioning-delete-modal';

describe('SsoProvisioningDeleteModal', () => {
const mockOnOpenChange = vi.fn();
const mockOnConfirm = vi.fn();

const defaultProps = {
open: true,
onOpenChange: mockOnOpenChange,
onConfirm: mockOnConfirm,
isLoading: false,
};

// Mock hooks
vi.mock('../../../../../../hooks/use-translator', () => ({
useTranslator: () => ({
t: (key: string, params?: any) => {

Check warning on line 21 in packages/react/src/components/my-org/idp-management/sso-provider-edit/sso-provisioning/__tests__/sso-provisioning-delete-modal.test.tsx

View workflow job for this annotation

GitHub Actions / 🔄 Continuous Integration

Unexpected any. Specify a different type
if (key === 'delete_button_label') return 'Delete';
if (key === 'remove_button_label') return 'Remove';
if (key === 'title' && params?.providerName) return `Delete ${params.providerName}`;
return key;
},
}),
}));

beforeEach(() => {
vi.clearAllMocks();
});

it('should render modal with correct title and description', () => {
render(<SsoProvisioningDeleteModal {...defaultProps} />);

expect(screen.getByText('modal.title')).toBeInTheDocument();
expect(screen.getByText('modal.content.description')).toBeInTheDocument();
});

it('should render cancel and delete buttons', () => {
render(<SsoProvisioningDeleteModal {...defaultProps} />);

expect(screen.getByText('modal.actions.cancel_button_label')).toBeInTheDocument();
expect(screen.getByText('modal.actions.delete_button_label')).toBeInTheDocument();
});

it('should call onOpenChange with false when cancel button is clicked', async () => {
const user = userEvent.setup();
render(<SsoProvisioningDeleteModal {...defaultProps} />);

const cancelButton = screen.getByText('modal.actions.cancel_button_label');

await user.click(cancelButton);

await waitFor(() => {
expect(mockOnOpenChange).toHaveBeenCalledWith(false);
});
});

it('should call onConfirm when delete button is clicked', async () => {
const user = userEvent.setup();
render(<SsoProvisioningDeleteModal {...defaultProps} />);

const deleteButton = screen.getByText('modal.actions.delete_button_label');

await user.click(deleteButton);

await waitFor(() => {
expect(mockOnConfirm).toHaveBeenCalled();
});
});

it('should disable buttons when isLoading is true', () => {
render(<SsoProvisioningDeleteModal {...defaultProps} isLoading={true} />);

const cancelButton = screen.getByText('modal.actions.cancel_button_label');
expect(cancelButton).toBeDisabled();
});

it('should not render modal when open is false', () => {
render(<SsoProvisioningDeleteModal {...defaultProps} open={false} />);
expect(screen.queryByText('modal.title')).not.toBeInTheDocument();
});
});
Loading
Loading