diff --git a/cypress/fixtures/items.js b/cypress/fixtures/items.js index d8496fe5c..2068b975d 100644 --- a/cypress/fixtures/items.js +++ b/cypress/fixtures/items.js @@ -1,16 +1,10 @@ import { ITEM_TYPES } from '../../src/config/constants'; - -export const MEMBERS = { - ANNA: { id: 'anna-id', name: 'anna', email: 'anna@email.com' }, - BOB: { id: 'bob-id', name: 'bob', email: 'bob@email.com' }, -}; - -export const CURRENT_USER_ID = MEMBERS.ANNA.id; +import { CURRENT_USER } from './members'; const DEFAULT_ITEM = { description: '', extra: {}, - creator: CURRENT_USER_ID, + creator: CURRENT_USER.id, type: ITEM_TYPES.SPACE, }; diff --git a/cypress/fixtures/members.js b/cypress/fixtures/members.js new file mode 100644 index 000000000..ea7c44085 --- /dev/null +++ b/cypress/fixtures/members.js @@ -0,0 +1,6 @@ +export const MEMBERS = { + ANNA: { id: 'anna-id', name: 'anna', email: 'anna@email.com' }, + BOB: { id: 'bob-id', name: 'bob', email: 'bob@email.com' }, +}; + +export const CURRENT_USER = MEMBERS.ANNA; diff --git a/cypress/integration/authentication.spec.js b/cypress/integration/authentication.spec.js new file mode 100644 index 000000000..eb4791daa --- /dev/null +++ b/cypress/integration/authentication.spec.js @@ -0,0 +1,74 @@ +import { + buildItemPath, + HOME_PATH, + SHARED_ITEMS_PATH, +} from '../../src/config/paths'; +import { + HEADER_APP_BAR_ID, + HEADER_USER_ID, + USER_MENU_SIGN_OUT_OPTION_ID, +} from '../../src/config/selectors'; +import { SAMPLE_ITEMS } from '../fixtures/items'; +import { CURRENT_USER } from '../fixtures/members'; + +const PAGE_LOAD_WAITING_TIME = 3000; + +describe('Authentication', () => { + describe('Signed Off > Redirect to sign in route', () => { + beforeEach(() => { + cy.setUpApi({ items: SAMPLE_ITEMS, getCurrentMemberError: true }); + }); + it('Home', () => { + cy.visit(HOME_PATH); + cy.wait('@signInRedirection'); + }); + it('Shared Items', () => { + cy.visit(SHARED_ITEMS_PATH); + cy.wait('@signInRedirection'); + }); + it('Item', () => { + cy.visit(buildItemPath(SAMPLE_ITEMS[0].id)); + cy.wait('@signInRedirection'); + }); + }); + + describe('Signed In', () => { + beforeEach(() => { + cy.setUpApi({ items: SAMPLE_ITEMS }); + }); + + it('Signing Off redirect to sign in route', () => { + cy.visit(HOME_PATH); + // user name in header + cy.get(`#${HEADER_USER_ID}`).click(); + cy.get(`#${USER_MENU_SIGN_OUT_OPTION_ID}`).click(); + cy.wait('@signInRedirection'); + }); + + describe('Load page correctly', () => { + it('Home', () => { + cy.visit(HOME_PATH); + cy.wait(PAGE_LOAD_WAITING_TIME); + cy.get(`#${HEADER_APP_BAR_ID}`).should('exist'); + }); + it('Shared Items', () => { + cy.visit(SHARED_ITEMS_PATH); + cy.wait(PAGE_LOAD_WAITING_TIME); + cy.get(`#${HEADER_APP_BAR_ID}`).should('exist'); + }); + it('Item', () => { + cy.visit(buildItemPath(SAMPLE_ITEMS[0].id)); + cy.wait(PAGE_LOAD_WAITING_TIME); + cy.get(`#${HEADER_APP_BAR_ID}`).should('exist'); + }); + }); + + describe('Display User Information', () => { + it('Header', () => { + cy.visit(HOME_PATH); + // user name in header + cy.get(`#${HEADER_USER_ID}`).should('contain', CURRENT_USER.name); + }); + }); + }); +}); diff --git a/cypress/integration/item/share/gridShareItem.spec.js b/cypress/integration/item/share/gridShareItem.spec.js index f617a6020..665072869 100644 --- a/cypress/integration/item/share/gridShareItem.spec.js +++ b/cypress/integration/item/share/gridShareItem.spec.js @@ -4,7 +4,8 @@ import { buildItemCard, SHARE_ITEM_BUTTON_CLASS, } from '../../../../src/config/selectors'; -import { MEMBERS, SAMPLE_ITEMS } from '../../../fixtures/items'; +import { SAMPLE_ITEMS } from '../../../fixtures/items'; +import { MEMBERS } from '../../../fixtures/members'; const shareItem = ({ id, member, permission }) => { cy.get(`#${buildItemCard(id)} .${SHARE_ITEM_BUTTON_CLASS}`).click(); diff --git a/cypress/integration/item/share/listShareItem.spec.js b/cypress/integration/item/share/listShareItem.spec.js index c30958e49..e53661e36 100644 --- a/cypress/integration/item/share/listShareItem.spec.js +++ b/cypress/integration/item/share/listShareItem.spec.js @@ -8,7 +8,8 @@ import { buildItemsTableRowId, SHARE_ITEM_BUTTON_CLASS, } from '../../../../src/config/selectors'; -import { MEMBERS, SAMPLE_ITEMS } from '../../../fixtures/items'; +import { SAMPLE_ITEMS } from '../../../fixtures/items'; +import { MEMBERS } from '../../../fixtures/members'; const shareItem = ({ id, member, permission }) => { cy.get(`#${buildItemsTableRowId(id)} .${SHARE_ITEM_BUTTON_CLASS}`).click(); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index dfd29dd11..f4ab96e93 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -22,6 +22,8 @@ import { mockGetS3FileContent, mockUploadItem, mockGetCurrentMember, + mockSignInRedirection, + mockSignOut, } from './server'; import './commands/item'; import './commands/navigation'; @@ -80,6 +82,10 @@ Cypress.Commands.add( mockGetS3FileContent(getS3FileContentError); mockGetCurrentMember(getCurrentMemberError); + + mockSignInRedirection(); + + mockSignOut(); }, ); diff --git a/cypress/support/server.js b/cypress/support/server.js index 7a806a810..5e8a50dcd 100644 --- a/cypress/support/server.js +++ b/cypress/support/server.js @@ -18,6 +18,8 @@ import { buildGetS3MetadataRoute, buildS3UploadFileRoute, GET_CURRENT_MEMBER_ROUTE, + buildSignInPath, + SIGN_OUT_ROUTE, } from '../../src/api/routes'; import { getItemById, @@ -25,7 +27,7 @@ import { isRootItem, transformIdForPath, } from '../../src/utils/item'; -import { CURRENT_USER_ID, MEMBERS } from '../fixtures/items'; +import { CURRENT_USER, MEMBERS } from '../fixtures/members'; import { ID_FORMAT, parseStringToRegExp, EMAIL_FORMAT } from './utils'; import { DEFAULT_PATCH, @@ -37,7 +39,7 @@ import { const API_HOST = Cypress.env('API_HOST'); const S3_FILES_HOST = Cypress.env('S3_FILES_HOST'); -export const mockGetCurrentMember = (members, shouldThrowError = false) => { +export const mockGetCurrentMember = (shouldThrowError = false) => { cy.intercept( { method: DEFAULT_GET.method, @@ -89,7 +91,7 @@ export const mockPostItem = (items, shouldThrowError) => { ...body, id, path: transformIdForPath(id), - creator: CURRENT_USER_ID, + creator: CURRENT_USER.id, }); }, ).as('postItem'); @@ -383,3 +385,33 @@ export const mockGetS3FileContent = (items, shouldThrowError) => { }, ).as('getS3FileContent'); }; + +export const mockSignInRedirection = () => { + cy.intercept( + { + method: DEFAULT_GET.method, + url: new RegExp(buildSignInPath()), + }, + ({ reply }) => { + reply({ + headers: { 'content-type': 'text/html' }, + statusCode: StatusCodes.OK, + }); + }, + ).as('signInRedirection'); +}; + +export const mockSignOut = () => { + cy.intercept( + { + method: DEFAULT_GET.method, + url: new RegExp(SIGN_OUT_ROUTE), + }, + ({ reply }) => { + reply({ + headers: { 'content-type': 'text/html' }, + statusCode: StatusCodes.OK, + }); + }, + ).as('signOut'); +}; diff --git a/src/api/authentication.js b/src/api/authentication.js index a8cc3826d..4b700e9f7 100644 --- a/src/api/authentication.js +++ b/src/api/authentication.js @@ -1,8 +1,11 @@ import { API_HOST } from '../config/constants'; +import { SIGN_OUT_ROUTE } from './routes'; import { DEFAULT_GET, checkRequest } from './utils'; // eslint-disable-next-line import/prefer-default-export export const signOut = async () => { - const req = await fetch(`${API_HOST}/logout`, DEFAULT_GET).then(checkRequest); + const req = await fetch(`${API_HOST}/${SIGN_OUT_ROUTE}`, DEFAULT_GET).then( + checkRequest, + ); return req.ok; }; diff --git a/src/api/routes.js b/src/api/routes.js index 7c139a249..4772261ba 100644 --- a/src/api/routes.js +++ b/src/api/routes.js @@ -1,3 +1,4 @@ +import qs from 'qs'; import { S3_FILES_HOST } from '../config/constants'; export const ITEMS_ROUTE = 'items'; @@ -39,7 +40,7 @@ export const buildGetS3MetadataRoute = (id) => export const buildS3FileUrl = (key) => `${S3_FILES_HOST}/${key}`; export const GET_CURRENT_MEMBER_ROUTE = `${MEMBERS_ROUTE}/current`; export const buildSignInPath = (to) => { - const qs = to ? `?to=${to}` : ''; - return `signin${qs}`; + const queryString = qs.stringify({ to }, { addQueryPrefix: true }); + return `signin${queryString}`; }; -export const SIGN_UP_PATH = 'signup'; +export const SIGN_OUT_ROUTE = 'logout'; diff --git a/src/components/common/SettingsHeader.js b/src/components/common/SettingsHeader.js index b3ebdfbc3..0943e6b74 100644 --- a/src/components/common/SettingsHeader.js +++ b/src/components/common/SettingsHeader.js @@ -15,6 +15,10 @@ import { USERNAME_MAX_LENGTH, } from '../../config/constants'; import { buildSignInPath } from '../../api/routes'; +import { + HEADER_USER_ID, + USER_MENU_SIGN_OUT_OPTION_ID, +} from '../../config/selectors'; const useStyles = makeStyles((theme) => ({ wrapper: { @@ -62,7 +66,11 @@ function SettingsHeader() { return {t('Sign In')}; } - return {t('Sign Out')}; + return ( + + {t('Sign Out')} + + ); }; const username = user.get('name'); @@ -71,7 +79,11 @@ function SettingsHeader() { return ( <> - + {username && ( diff --git a/src/components/layout/Header.js b/src/components/layout/Header.js index b179a7b02..cdc9c089e 100644 --- a/src/components/layout/Header.js +++ b/src/components/layout/Header.js @@ -9,6 +9,7 @@ import { AppBar, Toolbar, Typography } from '@material-ui/core'; import { ReactComponent as GraaspLogo } from '../../resources/graasp-logo.svg'; import { APP_NAME, HEADER_HEIGHT } from '../../config/constants'; import SettingsHeader from '../common/SettingsHeader'; +import { HEADER_APP_BAR_ID } from '../../config/selectors'; const useStyles = makeStyles((theme) => ({ header: { @@ -55,7 +56,7 @@ const Header = ({ isMenuOpen, toggleMenu }) => { }; return ( - +
{renderMenuIcon()} diff --git a/src/config/selectors.js b/src/config/selectors.js index ef9244e88..bb64215ea 100644 --- a/src/config/selectors.js +++ b/src/config/selectors.js @@ -49,3 +49,6 @@ export const CREATE_ITEM_FILE_ID = 'createItemFile'; export const ITEM_FORM_LINK_INPUT_ID = 'itemFormLinkInput'; export const DASHBOARD_UPLOADER_ID = 'dashboardUploader'; export const CREATE_ITEM_CLOSE_BUTTON_ID = 'createItemCloseButton'; +export const HEADER_APP_BAR_ID = 'headerAppBar'; +export const HEADER_USER_ID = 'headerUser'; +export const USER_MENU_SIGN_OUT_OPTION_ID = 'userMenuSignOutOption';