Skip to content

Commit

Permalink
fix: public alert (#168)
Browse files Browse the repository at this point in the history
* fix: public access

* fix: add tests

* fix: tests
  • Loading branch information
spaenleh authored Mar 28, 2024
1 parent 6260eb3 commit 79c7adf
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 69 deletions.
18 changes: 18 additions & 0 deletions cypress/e2e/PlayerView.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
dataCyWrapper,
inputTextFieldId,
inputTextFieldSelector,
publicAlertBannerIdCypress,
saveButtonCypress,
} from '../../src/config/selectors';
import { MEMBERS } from '../fixtures/members';
Expand Down Expand Up @@ -80,4 +81,21 @@ describe('<PlayerView />', () => {
);
});
});
describe('Public Access', () => {
beforeEach(() => {
cy.setUpApi({
appContext: {
context: Context.Player,
permission: undefined,
memberId: null,
},
});
cy.visit('/');
});

it('Shows public alert', () => {
cy.get(`#${publicAlertBannerIdCypress}`).should('be.visible');
cy.get(dataCyWrapper(saveButtonCypress)).should('be.disabled');
});
});
});
40 changes: 23 additions & 17 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,31 @@
import { inputTextFieldSelector } from '../../src/config/selectors';
import { MEMBERS } from '../fixtures/members';
import { mockItem, defaultMockContext } from '../../src/mocks/db';
import { SetupAPI } from '.';

Cypress.Commands.add('setUpApi', ({ database = {}, appContext } = {}) => {
// mock api and database
const fullAppContext = {
...defaultMockContext,
...appContext,
};
Cypress.on('window:before:load', (win) => {
win.database = {
appData: [],
appActions: [],
appSettings: [],
members: Object.values(MEMBERS),
items: [mockItem],
...database,
Cypress.Commands.add(
'setUpApi',
({ database = {}, appContext }: SetupAPI = {}) => {
// mock api and database
const fullAppContext = {
...defaultMockContext,
...appContext,
};
win.appContext = fullAppContext;
});
});
Cypress.on('window:before:load', (win) => {
win.database = {
appData: [],
appActions: [],
appSettings: [],
members: Object.values(MEMBERS),
items: [mockItem],
...database,
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
win.appContext = fullAppContext;
});
}
);

Cypress.Commands.add('enterStudentResponse', (text) => {
const input = cy.get(inputTextFieldSelector);
Expand Down
10 changes: 5 additions & 5 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Database, LocalContext } from '@graasp/apps-query-client';

export type SetupAPI = {
database?: Partial<Database>;
appContext?: Partial<LocalContext | { memberId: string | null }>;
};
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
Expand All @@ -8,10 +11,7 @@ declare global {
* Custom command to select DOM element by data-cy attribute.
* @example cy.dataCy('greeting')
*/
setUpApi(value?: {
database?: Partial<Database>;
appContext?: Partial<LocalContext>;
}): Chainable<JQuery<HTMLElement>>;
setUpApi(value?: SetupAPI): Chainable<JQuery<HTMLElement>>;
enterStudentResponse(value: string): Chainable<JQuery<HTMLElement>>;
clearStudentResponse(): Chainable<JQuery<HTMLElement>>;
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"hooks:install": "husky install",
"hooks:uninstall": "husky uninstall",
"pre-commit": "yarn prettier:check && yarn lint",
"cypress:open": "env-cmd -f ./.env.development cypress open",
"cypress:open": "env-cmd -f ./.env.test cypress open",
"test": "concurrently -k -s first \"yarn start:test\" \"yarn test:ci\" ",
"test:ci": "env-cmd -f ./.env.test cypress run --browser chrome --headless && nyc report --reporter=text --reporter=text-summary",
"cov:report": "open ./coverage/lcov-report/index.html"
Expand Down
131 changes: 88 additions & 43 deletions src/components/views/read/PlayerView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Grid from '@mui/material/Grid';
import TextField, { TextFieldProps } from '@mui/material/TextField';
import TextField from '@mui/material/TextField';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, styled } from '@mui/material';
Expand All @@ -8,7 +8,11 @@ import Loader from '../../common/Loader';
import { MAX_INPUT_LENGTH, MAX_ROWS } from '../../../config/settings';
import { mutations, hooks } from '../../../config/queryClient';
import SaveButton from './SaveButton';
import { inputCypress, inputTextFieldId } from '../../../config/selectors';
import {
inputCypress,
inputTextFieldId,
publicAlertBannerIdCypress,
} from '../../../config/selectors';
import { ACTION_TYPES } from '../../../config/actionTypes';
import { APP_DATA_TYPES } from '../../../config/appDataTypes';

Expand All @@ -29,10 +33,70 @@ const MainContainer = styled(Grid)(({ theme }) => ({
padding: theme.spacing(1),
}));

type PlayerViewComponentProps = {
isLoggedIn: boolean;
isSaveButtonDisabled: boolean;
text: string;
onChangeText?: (value: string) => void;
onSave?: () => void;
helperText?: string;
autoFocusTextField?: boolean;
};

const PlayerViewComponent = ({
isLoggedIn,
isSaveButtonDisabled,
text,
onChangeText,
onSave,
helperText,
autoFocusTextField = false,
}: PlayerViewComponentProps) => {
const { t } = useTranslation();
return (
<Grid container spacing={0}>
<MainContainer item xs={12}>
{!isLoggedIn && (
<Alert severity="error" id={publicAlertBannerIdCypress}>
{t(
'You cannot answer if you are not authenticated. Please log in.'
)}
</Alert>
)}
<FormContainer noValidate autoComplete="off">
<StyledTextField
autoFocus={autoFocusTextField}
inputProps={{
maxLength: MAX_INPUT_LENGTH,
}}
disabled={!isLoggedIn}
data-cy={inputCypress}
id={inputTextFieldId}
label={t('Type Here')}
multiline
maxRows={MAX_ROWS}
value={text}
onChange={(e) => onChangeText?.(e.target.value)}
margin="normal"
helperText={helperText}
variant="outlined"
fullWidth
/>
</FormContainer>
<SaveButton disabled={isSaveButtonDisabled} onClick={onSave} />
</MainContainer>
</Grid>
);
};

const PlayerView = (): JSX.Element => {
const { t } = useTranslation();
const [text, setText] = useState('');
const { data: appData, isLoading: isAppDataLoading } = hooks.useAppData();
const {
data: appData,
isLoading: isAppDataLoading,
isSuccess: isAppDataSuccess,
} = hooks.useAppData();
const { mutate: postAppData } = mutations.usePostAppData();
const { mutate: patchAppData } = mutations.usePatchAppData();
const { mutate: postAction } = mutations.usePostAppAction();
Expand All @@ -55,14 +119,12 @@ const PlayerView = (): JSX.Element => {
}
}, [inputResource]);

if (appData) {
const feedbackResource = appData.find(
if (appData || isAppDataSuccess) {
const feedbackResource = appData?.find(
({ type, member }) =>
type === APP_DATA_TYPES.FEEDBACK && memberId === member?.id
);

const handleChangeText: TextFieldProps['onChange'] = ({ target }) => {
const { value } = target;
const handleChangeText = (value: string) => {
setText(value);
};

Expand Down Expand Up @@ -92,53 +154,36 @@ const PlayerView = (): JSX.Element => {
if (feedbackResource) {
return `${t('Feedback')}: ${feedbackResource.data?.text}`;
}
return null;
return undefined;
};

const textIsDifferent = text === inputResource?.data?.text;

return (
<Grid container spacing={0}>
<MainContainer item xs={12}>
{Boolean(!memberId) && (
<Alert severity="error">
{t('You cannot answer if you are not authenticated')}
</Alert>
)}
<FormContainer noValidate autoComplete="off">
<StyledTextField
autoFocus={context?.standalone}
inputProps={{
maxLength: MAX_INPUT_LENGTH,
}}
disabled={Boolean(!memberId)}
data-cy={inputCypress}
id={inputTextFieldId}
label={t('Type Here')}
multiline
maxRows={MAX_ROWS}
value={text}
onChange={handleChangeText}
margin="normal"
helperText={buildFeedbackText()}
variant="outlined"
fullWidth
/>
</FormContainer>
<SaveButton
disabled={textIsDifferent}
onClick={handleClickSaveText}
/>
</MainContainer>
</Grid>
<PlayerViewComponent
text={text}
helperText={buildFeedbackText()}
onChangeText={handleChangeText}
onSave={handleClickSaveText}
isSaveButtonDisabled={textIsDifferent}
isLoggedIn={Boolean(memberId)}
autoFocusTextField={context?.standalone}
/>
);
}

if (isAppDataLoading) {
return <Loader />;
}

return <Alert severity="error">Could not get App Data</Alert>;
return (
<PlayerViewComponent
text=""
helperText={t('You need to login to use this app')}
isSaveButtonDisabled
isLoggedIn={false}
/>
);
};

export default PlayerView;
2 changes: 1 addition & 1 deletion src/components/views/read/SaveButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Container = styled('div')(({ theme }) => ({

type SaveButtonProps = {
disabled: boolean;
onClick: () => void;
onClick?: () => void;
};

const SaveButton = ({ disabled, onClick }: SaveButtonProps) => {
Expand Down
1 change: 1 addition & 0 deletions src/config/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const editFeedbackButtonCypress = 'editFeedbackButton';
export const feedbackInputCypress = 'feedbackInput';
export const submitButtonCypress = 'submitButton';
export const deleteConfirmButtonCypress = 'deleteConfirmButton';
export const publicAlertBannerIdCypress = 'publicAlertBanner';

export const dataCyWrapper = (cypressSelector: string) =>
`[data-cy=${cypressSelector}]`;
3 changes: 2 additions & 1 deletion src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"Settings": "Settings",
"Show Header to Students": "Show Header to Students",
"Anonymous": "Anonymous",
"Actions": "Actions"
"Actions": "Actions",
"You cannot answer if you are not authenticated. Please log in.": "You cannot answer if you are not authenticated. Please log in."
}
}
2 changes: 1 addition & 1 deletion src/window.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Database, LocalContext } from '@graasp/apps-query-client';

declare global {
interface Window {
appContext: LocalContext;
appContext: LocalContext & { memberId: null };
Cypress: boolean;
database: Database;
apiErrors: object;
Expand Down

0 comments on commit 79c7adf

Please sign in to comment.