diff --git a/frontend/src/API.ts b/frontend/src/API.ts
index 4ca97f79a..4dc094128 100644
--- a/frontend/src/API.ts
+++ b/frontend/src/API.ts
@@ -57,12 +57,14 @@ export const useParticipantScores = () =>
export const useParticipantLink = () =>
useGet(API_BASE_URL + URLS.participant.link);
+type ConsentResponse = boolean | null;
+
export const useConsent = (slug: string) =>
- useGet(API_BASE_URL + URLS.result.get('consent_' + slug));
+ useGet(API_BASE_URL + URLS.result.get('consent_' + slug));
interface CreateConsentParams {
block: Block;
- participant: Participant;
+ participant: Pick;
}
/** Create consent for given experiment */
diff --git a/frontend/src/components/Consent/Consent.test.jsx b/frontend/src/components/Consent/Consent.test.tsx
similarity index 52%
rename from frontend/src/components/Consent/Consent.test.jsx
rename to frontend/src/components/Consent/Consent.test.tsx
index c9686611e..153874fff 100644
--- a/frontend/src/components/Consent/Consent.test.jsx
+++ b/frontend/src/components/Consent/Consent.test.tsx
@@ -1,9 +1,8 @@
-import React from 'react';
import { fireEvent, render, waitFor } from '@testing-library/react';
-import Consent from './Consent';
+import Consent, { ConsentProps } from './Consent';
import { useConsent } from '../../API'
import { saveAs } from 'file-saver';
-import { vi } from 'vitest';
+import { vi, Mock, expect, it, describe, } from 'vitest';
global.Blob = vi.fn().mockImplementation((content, options) => ({
content,
@@ -26,51 +25,60 @@ const mockBlock = {
loading_text: 'Loading...',
};
+const getConsentProps: (overrides?: Partial) => ConsentProps = (overrides) => ({
+ title: 'Consent',
+ text: 'Consent Text
',
+ block: mockBlock,
+ participant: { csrf_token: '42' },
+ onNext: vi.fn(),
+ confirm: 'Agree',
+ deny: 'Disagree',
+ ...overrides,
+});
+
describe('Consent', () => {
it('renders loading state correctly', () => {
- useConsent.mockReturnValue([null, true]); // Mock loading state
- const { getByText } = render();
- expect(document.body.contains(getByText('Loading...'))).to.be.true;
+ (useConsent as Mock).mockReturnValue([null, true]); // Mock loading state
+ const { getByText } = render();
+ expect(document.body.contains(getByText('Loading...'))).toBe(true);
});
it('renders consent text when not loading', () => {
- useConsent.mockReturnValue([null, false]);
- const { getByText } = render();
- expect(document.body.contains(getByText('Consent Text'))).to.be.true;
+ (useConsent as Mock).mockReturnValue([null, false]);
+ const { getByText } = render(Consent Text
', block: { slug: 'test-experiment', loading_text: 'Loading...' } })} />);
+
+ expect(document.body.contains(getByText('Consent Text'))).toBe(true);
});
it('calls onNext when Agree button is clicked', async () => {
- useConsent.mockReturnValue([null, false]);
+ (useConsent as Mock).mockReturnValue([null, false]);
const onNext = vi.fn();
- const { getByText } = render();
+ const { getByText } = render();
fireEvent.click(getByText('Agree'));
await waitFor(() => expect(onNext).toHaveBeenCalled());
});
it('triggers download when Download button is clicked', async () => {
- useConsent.mockReturnValue([null, false]);
- const { getByTestId } = render();
+ (useConsent as Mock).mockReturnValue([null, false]);
+ const { getByTestId } = render();
fireEvent.click(getByTestId('download-button'));
await waitFor(() => expect(saveAs).toHaveBeenCalled());
});
it('auto advances if consent is already given', () => {
- useConsent.mockReturnValue([true, false]);
+ (useConsent as Mock).mockReturnValue([true, false]);
const onNext = vi.fn();
- render();
+ render();
expect(onNext).toHaveBeenCalled();
});
it('calculates style for consent text correctly', () => {
- useConsent.mockReturnValue([null, false]);
+ (useConsent as Mock).mockReturnValue([null, false]);
Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: 800 });
- const { getByTestId } = render();
+ const { getByTestId } = render();
const consentText = getByTestId('consent-text');
expect(consentText.style.height).toBe('500px');
});
-
-
-
});
diff --git a/frontend/src/components/Consent/Consent.jsx b/frontend/src/components/Consent/Consent.tsx
similarity index 88%
rename from frontend/src/components/Consent/Consent.jsx
rename to frontend/src/components/Consent/Consent.tsx
index da7641749..8f0a77bb3 100644
--- a/frontend/src/components/Consent/Consent.jsx
+++ b/frontend/src/components/Consent/Consent.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import { useEffect } from "react";
import { saveAs } from 'file-saver';
import { URLS } from "@/config";
@@ -6,9 +6,20 @@ import Button from "../Button/Button";
import Loading from "../Loading/Loading";
import { createConsent, useConsent } from "../../API";
import classNames from "classnames";
+import Participant from "@/types/Participant";
+
+export interface ConsentProps {
+ title: string;
+ text: string;
+ block: any;
+ participant: Pick;
+ onNext: () => void;
+ confirm: string;
+ deny: string;
+}
/** Consent is an block view that shows the consent text, and handles agreement/stop actions */
-const Consent = ({ title, text, block, participant, onNext, confirm, deny }) => {
+const Consent = ({ title, text, block, participant, onNext, confirm, deny }: ConsentProps) => {
const [consent, loadingConsent] = useConsent(block.slug);
const urlQueryString = window.location.search;
@@ -30,7 +41,7 @@ const Consent = ({ title, text, block, participant, onNext, confirm, deny }) =>
const onDownload = async () => {
const doc = new DOMParser().parseFromString(text, 'text/html');
- const txt = doc.body.textContent.split(' ').join('');
+ const txt = doc.body.textContent ? doc.body.textContent.split(' ').join('') : '';
const blob = new Blob([txt], { type: "text/plain;charset=utf-8" });
saveAs(blob, 'consent.txt');
}