225 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| import { within } from '@testing-library/dom';
 | |
| import htmlMergeRequestWithMentions from 'test_fixtures/merge_requests/merge_request_with_mentions.html';
 | |
| import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
 | |
| import UsersCache from '~/lib/utils/users_cache';
 | |
| import initUserPopovers from '~/user_popovers';
 | |
| import waitForPromises from 'helpers/wait_for_promises';
 | |
| 
 | |
| jest.mock('~/api/user_api', () => ({
 | |
|   followUser: jest.fn().mockResolvedValue({}),
 | |
|   unfollowUser: jest.fn().mockResolvedValue({}),
 | |
| }));
 | |
| 
 | |
| describe('User Popovers', () => {
 | |
|   const selector = '.js-user-link[data-user], .js-user-link[data-user-id]';
 | |
|   const findFixtureLinks = () => Array.from(document.querySelectorAll(selector));
 | |
|   const createUserLink = () => {
 | |
|     const link = document.createElement('a');
 | |
| 
 | |
|     link.classList.add('js-user-link');
 | |
|     link.dataset.user = '1';
 | |
| 
 | |
|     return link;
 | |
|   };
 | |
|   const findPopovers = () => {
 | |
|     return Array.from(document.querySelectorAll('[data-testid="user-popover"]'));
 | |
|   };
 | |
| 
 | |
|   const dummyUser = { name: 'root', username: 'root', is_followed: false };
 | |
|   const dummyUserStatus = { message: 'active' };
 | |
| 
 | |
|   const triggerEvent = (eventName, el) => {
 | |
|     const event = new MouseEvent(eventName, {
 | |
|       bubbles: true,
 | |
|       cancelable: true,
 | |
|       view: window,
 | |
|     });
 | |
| 
 | |
|     el.dispatchEvent(event);
 | |
|   };
 | |
| 
 | |
|   const setupTestSubject = () => {
 | |
|     setHTMLFixture(htmlMergeRequestWithMentions);
 | |
| 
 | |
|     const usersCacheSpy = () => Promise.resolve(dummyUser);
 | |
|     jest.spyOn(UsersCache, 'retrieveById').mockImplementation((userId) => usersCacheSpy(userId));
 | |
| 
 | |
|     const userStatusCacheSpy = () => Promise.resolve(dummyUserStatus);
 | |
|     jest
 | |
|       .spyOn(UsersCache, 'retrieveStatusById')
 | |
|       .mockImplementation((userId) => userStatusCacheSpy(userId));
 | |
|     jest.spyOn(UsersCache, 'updateById');
 | |
| 
 | |
|     initUserPopovers((popoverInstance) => {
 | |
|       const mountingRoot = document.createElement('div');
 | |
|       document.body.appendChild(mountingRoot);
 | |
|       popoverInstance.$mount(mountingRoot);
 | |
|     });
 | |
|   };
 | |
| 
 | |
|   describe('when signed out', () => {
 | |
|     beforeEach(() => {
 | |
|       setupTestSubject();
 | |
|     });
 | |
| 
 | |
|     it('does not show a placeholder popover on hover', () => {
 | |
|       const linksWithUsers = findFixtureLinks();
 | |
|       linksWithUsers.forEach((el) => {
 | |
|         triggerEvent('mouseover', el);
 | |
|       });
 | |
| 
 | |
|       expect(findPopovers().length).toBe(0);
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   describe('when signed in', () => {
 | |
|     beforeEach(() => {
 | |
|       window.gon.current_user_id = 7;
 | |
| 
 | |
|       setupTestSubject();
 | |
|     });
 | |
| 
 | |
|     afterEach(() => {
 | |
|       resetHTMLFixture();
 | |
|     });
 | |
| 
 | |
|     describe('shows a placeholder popover on hover', () => {
 | |
|       let linksWithUsers;
 | |
|       beforeEach(() => {
 | |
|         linksWithUsers = findFixtureLinks();
 | |
|         linksWithUsers.forEach((el) => {
 | |
|           triggerEvent('mouseover', el);
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       it('for initial links', () => {
 | |
|         expect(findPopovers().length).toBe(linksWithUsers.length);
 | |
|       });
 | |
| 
 | |
|       it('for elements added after initial load', () => {
 | |
|         const addedLinks = [createUserLink(), createUserLink()];
 | |
|         addedLinks.forEach((link) => {
 | |
|           document.body.appendChild(link);
 | |
|         });
 | |
| 
 | |
|         jest.runOnlyPendingTimers();
 | |
| 
 | |
|         addedLinks.forEach((link) => {
 | |
|           triggerEvent('mouseover', link);
 | |
|         });
 | |
| 
 | |
|         expect(findPopovers().length).toBe(linksWithUsers.length + addedLinks.length);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     it('does not initialize the popovers for group references', () => {
 | |
|       const [groupLink] = Array.from(document.querySelectorAll('.js-user-link[data-group]'));
 | |
| 
 | |
|       triggerEvent('mouseover', groupLink);
 | |
|       jest.runOnlyPendingTimers();
 | |
| 
 | |
|       expect(findPopovers().length).toBe(0);
 | |
|     });
 | |
| 
 | |
|     it('does not initialize the popovers for @all references', () => {
 | |
|       const [projectLink] = Array.from(document.querySelectorAll('.js-user-link[data-project]'));
 | |
| 
 | |
|       triggerEvent('mouseover', projectLink);
 | |
|       jest.runOnlyPendingTimers();
 | |
| 
 | |
|       expect(findPopovers().length).toBe(0);
 | |
|     });
 | |
| 
 | |
|     it('does not initialize the user popovers twice for the same element', () => {
 | |
|       const [firstUserLink] = findFixtureLinks();
 | |
|       triggerEvent('mouseover', firstUserLink);
 | |
|       jest.runOnlyPendingTimers();
 | |
|       triggerEvent('mouseleave', firstUserLink);
 | |
|       jest.runOnlyPendingTimers();
 | |
|       triggerEvent('mouseover', firstUserLink);
 | |
|       jest.runOnlyPendingTimers();
 | |
| 
 | |
|       expect(findPopovers().length).toBe(1);
 | |
|     });
 | |
| 
 | |
|     describe('when user link emits mouseenter event with empty user cache', () => {
 | |
|       let userLink;
 | |
| 
 | |
|       beforeEach(() => {
 | |
|         UsersCache.retrieveById.mockReset();
 | |
| 
 | |
|         [userLink] = findFixtureLinks();
 | |
| 
 | |
|         triggerEvent('mouseover', userLink);
 | |
|       });
 | |
| 
 | |
|       it('populates popover with preloaded user data', () => {
 | |
|         const { name, userId, username } = userLink.dataset;
 | |
| 
 | |
|         expect(userLink.user).toEqual(
 | |
|           expect.objectContaining({
 | |
|             name,
 | |
|             userId,
 | |
|             username,
 | |
|           }),
 | |
|         );
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     describe('when user link emits mouseenter event', () => {
 | |
|       let userLink;
 | |
| 
 | |
|       beforeEach(() => {
 | |
|         [userLink] = findFixtureLinks();
 | |
| 
 | |
|         triggerEvent('mouseover', userLink);
 | |
|       });
 | |
| 
 | |
|       it('removes title attribute from user links', () => {
 | |
|         expect(userLink.getAttribute('title')).toBe('');
 | |
|         expect(userLink.dataset.originalTitle).toBe('');
 | |
|       });
 | |
| 
 | |
|       it('fetches user info and status from the user cache', () => {
 | |
|         const { userId } = userLink.dataset;
 | |
| 
 | |
|         expect(UsersCache.retrieveById).toHaveBeenCalledWith(userId);
 | |
|         expect(UsersCache.retrieveStatusById).toHaveBeenCalledWith(userId);
 | |
|       });
 | |
| 
 | |
|       it('removes aria-describedby attribute from the user link on mouseleave', () => {
 | |
|         userLink.setAttribute('aria-describedby', 'popover');
 | |
|         triggerEvent('mouseleave', userLink);
 | |
| 
 | |
|         expect(userLink.getAttribute('aria-describedby')).toBe(null);
 | |
|       });
 | |
| 
 | |
|       it('updates toggle follow button and `UsersCache` when toggle follow button is clicked', async () => {
 | |
|         const [firstPopover] = findPopovers();
 | |
|         const withinFirstPopover = within(firstPopover);
 | |
|         const findFollowButton = () => withinFirstPopover.queryByRole('button', { name: 'Follow' });
 | |
|         const findUnfollowButton = () =>
 | |
|           withinFirstPopover.queryByRole('button', { name: 'Unfollow' });
 | |
| 
 | |
|         jest.runOnlyPendingTimers();
 | |
| 
 | |
|         const { userId } = document.querySelector(selector).dataset;
 | |
| 
 | |
|         triggerEvent('click', findFollowButton());
 | |
| 
 | |
|         await waitForPromises();
 | |
| 
 | |
|         expect(findUnfollowButton()).not.toBe(null);
 | |
|         expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: true });
 | |
| 
 | |
|         triggerEvent('click', findUnfollowButton());
 | |
| 
 | |
|         await waitForPromises();
 | |
| 
 | |
|         expect(findFollowButton()).not.toBe(null);
 | |
|         expect(UsersCache.updateById).toHaveBeenCalledWith(userId, { is_followed: false });
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| });
 |