From 4ba3a5cff178ca8c6486b0188ef610ca1b2984dc Mon Sep 17 00:00:00 2001 From: sailingsam Date: Mon, 23 Sep 2024 03:32:13 +0530 Subject: [PATCH 1/5] Ready for review --- groups/index.html | 11 ++++++++ groups/script.js | 68 ++++++++++++++++++++++++++++++++++++++++++++--- groups/utils.js | 13 +++++---- 3 files changed, 83 insertions(+), 9 deletions(-) diff --git a/groups/index.html b/groups/index.html index e354491b..496a1bd4 100644 --- a/groups/index.html +++ b/groups/index.html @@ -44,6 +44,17 @@
+
diff --git a/groups/script.js b/groups/script.js index f7edcfa7..4dbcbfa3 100644 --- a/groups/script.js +++ b/groups/script.js @@ -15,7 +15,7 @@ import { import { addGroupRoleToMember, createDiscordGroupRole, - getDiscordGroups, + getPaginatedDiscordGroups, getUserGroupRoles, getUserSelf, removeRoleFromMember, @@ -110,6 +110,8 @@ const handler = { obj[prop] = value; break; case 'discordId': + case 'isLoading': + case 'hasMoreGroups': obj[prop] = value; break; default: @@ -123,10 +125,12 @@ const dataStore = new Proxy( { userSelf: null, groups: null, - filteredGroupsIds: null, + filteredGroupsIds: [], search: isDev ? getParamValueFromURL(QUERY_PARAM_KEY.GROUP_SEARCH) : '', discordId: null, - isCreateGroupModalOpen: false, + isGroupCreationModalOpen: false, + isLoading: false, + hasMoreGroups: true, }, handler, ); @@ -147,6 +151,7 @@ const onCreate = () => { throw new Error(data); } dataStore.userSelf = data; + removeLoadingCards(); removeLoadingNavbarProfile(); await afterAuthentication(); }) @@ -165,10 +170,11 @@ const onCreate = () => { bindSearchInput(); bindSearchFocus(); bindGroupCreationButton(); + bindInfiniteScroll(); }; const afterAuthentication = async () => { renderNavbarProfile({ profile: dataStore.userSelf }); - await Promise.all([getDiscordGroups(), getUserGroupRoles()]).then( + await Promise.all([loadMoreGroups(), getUserGroupRoles()]).then( ([groups, roleData]) => { dataStore.filteredGroupsIds = groups.map((group) => group.id); dataStore.groups = groups.reduce((acc, group) => { @@ -198,9 +204,63 @@ const afterAuthentication = async () => { }, ); }; +const loadMoreGroups = async () => { + if (dataStore.isLoading || !dataStore.hasMoreGroups) return; + + dataStore.isLoading = true; + renderLoadingCards(); + + const newGroups = await getPaginatedDiscordGroups(); + + removeLoadingCards(); + dataStore.isLoading = false; + + if (newGroups.length === 0) { + dataStore.hasMoreGroups = false; + return; + } + + dataStore.groups = { + ...dataStore.groups, + ...newGroups.reduce((acc, group) => { + acc[group.id] = { + id: group.id, + title: group.rolename + .replace('group-', '') + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '), + count: group.memberCount, + isMember: group.isMember, + roleId: group.roleid, + description: group.description, + isUpdating: false, + }; + return acc; + }, {}), + }; + + dataStore.filteredGroupsIds = [ + ...dataStore.filteredGroupsIds, + ...newGroups.map((group) => group.id), + ]; + + return newGroups; +}; // Bind Functions +const bindInfiniteScroll = () => { + window.addEventListener('scroll', () => { + if ( + window.innerHeight + window.scrollY >= + document.body.offsetHeight - 100 + ) { + loadMoreGroups(); + } + }); +}; + const bindGroupCreationButton = () => { const groupCreationBtn = document.querySelector('.create-group'); diff --git a/groups/utils.js b/groups/utils.js index 4dfeff83..291fc669 100644 --- a/groups/utils.js +++ b/groups/utils.js @@ -1,4 +1,5 @@ -const BASE_URL = window.API_BASE_URL; // REPLACE WITH YOUR LOCALHOST URL FOR TESTING LOCAL BACKEND +// const BASE_URL = window.API_BASE_URL; // REPLACE WITH YOUR LOCALHOST URL FOR TESTING LOCAL BACKEND +const BASE_URL = "http://localhost:3000"; async function getMembers() { try { @@ -43,9 +44,10 @@ async function getUserGroupRoles() { return await res.json(); } -async function getDiscordGroups() { +let latestDoc = 0; +async function getPaginatedDiscordGroups() { try { - const res = await fetch(`${BASE_URL}/discord-actions/groups`, { + const res = await fetch(`${BASE_URL}/discord-actions/groups?latestDoc=${latestDoc}`, { method: 'GET', credentials: 'include', headers: { @@ -53,7 +55,8 @@ async function getDiscordGroups() { }, }); - const { groups } = await res.json(); + const { groups, newLatestDoc } = await res.json(); + latestDoc = newLatestDoc; return groups; } catch (err) { return err; @@ -160,7 +163,7 @@ export { getUserGroupRoles, getMembers, getUserSelf, - getDiscordGroups, + getPaginatedDiscordGroups, createDiscordGroupRole, addGroupRoleToMember, removeRoleFromMember, From 904d986b4470853f8da0e9f8c76f5310fe387803 Mon Sep 17 00:00:00 2001 From: sailingsam Date: Mon, 23 Sep 2024 20:49:11 +0530 Subject: [PATCH 2/5] added lazy load in groups page --- groups/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/groups/utils.js b/groups/utils.js index 291fc669..cb84a5eb 100644 --- a/groups/utils.js +++ b/groups/utils.js @@ -1,5 +1,5 @@ -// const BASE_URL = window.API_BASE_URL; // REPLACE WITH YOUR LOCALHOST URL FOR TESTING LOCAL BACKEND -const BASE_URL = "http://localhost:3000"; +const BASE_URL = window.API_BASE_URL; // REPLACE WITH YOUR LOCALHOST URL FOR TESTING LOCAL BACKEND +// const BASE_URL = "http://localhost:3000"; async function getMembers() { try { From e9d7841b8f2f8f81ada0693b8aca962e2a9bf2fc Mon Sep 17 00:00:00 2001 From: sailingsam Date: Wed, 25 Sep 2024 20:29:14 +0530 Subject: [PATCH 3/5] feat: lazy Load Discord Roles Page | Tests written --- __tests__/groups/group.test.js | 88 +++++++--- mock-data/groups/index.js | 282 ++++++++++++++++++++++++++++++--- 2 files changed, 332 insertions(+), 38 deletions(-) diff --git a/__tests__/groups/group.test.js b/__tests__/groups/group.test.js index 4400450f..dde3e11e 100644 --- a/__tests__/groups/group.test.js +++ b/__tests__/groups/group.test.js @@ -49,7 +49,10 @@ describe('Discord Groups Page', () => { }, body: JSON.stringify(allUsersData.users[0]), }); - } else if (url === `${BASE_URL}/discord-actions/groups`) { + } else if (url.startsWith(`${BASE_URL}/discord-actions/groups`)) { + const urlParams = new URLSearchParams(url.split('?')[1]); + const latestDoc = urlParams.get('latestDoc'); + const paginatedGroups = getPaginatedGroups(latestDoc); interceptedRequest.respond({ status: 200, contentType: 'application/json', @@ -58,18 +61,7 @@ describe('Discord Groups Page', () => { 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }, - body: JSON.stringify(discordGroups), - }); - } else if (url === `${BASE_URL}/discord-actions/groups?dev=true`) { - interceptedRequest.respond({ - status: 200, - contentType: 'application/json', - headers: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type, Authorization', - }, - body: JSON.stringify(discordGroups), + body: JSON.stringify(paginatedGroups), }); } else if (url === `${BASE_URL}/discord-actions/roles`) { interceptedRequest.respond({ @@ -89,7 +81,6 @@ describe('Discord Groups Page', () => { if (url === `${BASE_URL}/discord-actions/groups`) { const postData = interceptedRequest.postData(); const groupData = JSON.parse(postData); - // discordGroups.push(groupData); interceptedRequest.respond({ status: 201, contentType: 'application/json', @@ -134,7 +125,7 @@ describe('Discord Groups Page', () => { } }); await page.goto(`${PAGE_URL}/groups`); - await page.waitForNetworkIdle(); + await page.waitForSelector('.card', { timeout: 5000 }); // Wait for the first batch of cards to load }); afterAll(async () => { @@ -147,7 +138,6 @@ describe('Discord Groups Page', () => { }); test('Should display cards', async () => { - await page.waitForSelector('.card'); const cards = await page.$$('.card'); expect(cards.length).toBeGreaterThan(0); @@ -239,11 +229,56 @@ describe('Discord Groups Page', () => { const closeBtn = await groupCreationModal.$('#close-button'); await closeBtn.click(); + await page.waitForTimeout(500); // Wait for modal to close const groupCreationModalClosed = await page.$('.group-creation-modal'); expect(groupCreationModalClosed).toBeFalsy(); }); + test('Should load more groups on scroll', async () => { + await page.goto(`${PAGE_URL}/groups`); + await page.waitForSelector('.card', { timeout: 5000 }); + + const initialGroupCount = await page.$$eval('.card', (cards) => cards.length); + + await page.evaluate(() => { + window.scrollTo(0, document.body.scrollHeight); + }); + + await page.waitForFunction((initialCount) => { + return document.querySelectorAll('.card').length > initialCount; + }, {}, initialGroupCount); + + const newGroupCount = await page.$$eval('.card', (cards) => cards.length); + + expect(newGroupCount).toBeGreaterThan(initialGroupCount); + }); + + test('Should stop loading more groups when all groups are loaded', async () => { + await page.goto(`${PAGE_URL}/groups`); + await page.waitForSelector('.card', { timeout: 5000 }); + + // Scroll to the bottom multiple times + for (let i = 0; i < 5; i++) { + await page.evaluate(() => { + window.scrollTo(0, document.body.scrollHeight); + }); + await page.waitForTimeout(1000); + } + + const finalGroupCount = await page.$$eval('.card', (cards) => cards.length); + + // Scroll one more time + await page.evaluate(() => { + window.scrollTo(0, document.body.scrollHeight); + }); + await page.waitForTimeout(1000); + + const newFinalGroupCount = await page.$$eval('.card', (cards) => cards.length); + + expect(newFinalGroupCount).toBe(finalGroupCount); + }); + test('Should display only specified groups when dev=true and name= with different case', async () => { const groupNames = 'fIrSt,DSA+COdInG'; await page.goto(`${PAGE_URL}/groups?dev=true&name=${groupNames}`); @@ -255,15 +290,30 @@ describe('Discord Groups Page', () => { ); }); - expect(displayedGroups).toEqual(['First Daaa', 'DSA Coding Group']); + expect(displayedGroups).toContain('First Daaa'); + expect(displayedGroups).toContain('DSA Coding Group'); }); test('Should display no group found div when no group is present', async () => { await page.goto(`${PAGE_URL}/groups?dev=true&name=no-group-present`); - await page.waitForNetworkIdle(); + await page.waitForSelector('.no-group-container', { timeout: 5000 }); const noGroupDiv = await page.$('.no-group-container'); - expect(noGroupDiv).toBeTruthy(); }); }); + +// Helper function to simulate paginated data +function getPaginatedGroups(latestDoc) { + const pageSize = 18; + const startIndex = latestDoc ? discordGroups.groups.findIndex(g => g.id === latestDoc) + 1 : 0; + const endIndex = startIndex + pageSize; + const groups = discordGroups.groups.slice(startIndex, endIndex); + const newLatestDoc = groups.length > 0 ? groups[groups.length - 1].id : null; + + return { + message: 'Roles fetched successfully!', + groups, + newLatestDoc, + }; +} \ No newline at end of file diff --git a/mock-data/groups/index.js b/mock-data/groups/index.js index 15b86aba..388bb7e1 100644 --- a/mock-data/groups/index.js +++ b/mock-data/groups/index.js @@ -1,3 +1,58 @@ +// const discordGroups = { +// message: 'Roles fetched successfully!', +// groups: [ +// { +// id: 'CqnEhbwtCqdcZdlrixLn', +// date: { +// _seconds: 1683238758, +// _nanoseconds: 183000000, +// }, +// createdBy: 'V4rqL1aDecNGoa1IxiCu', +// rolename: 'group-first-daaa', +// roleid: '1103808103641780225', +// firstName: 'Test', +// lastName: 'User1', +// image: 'https://image.cdn.com/123dfg', +// memberCount: 2, +// }, +// { +// id: 'Mky71E6f6QWCY5MOBJFy', +// date: { +// _seconds: 1687619454, +// _nanoseconds: 560000000, +// }, +// createdBy: 'jbGcfZLGYjHwxQ1Zh8ZJ', +// rolename: 'group-DSA', +// roleid: '1122182070509244416', +// firstName: 'Test', +// lastName: 'User2', +// image: 'https://image.cdn.com/12dfg', +// memberCount: 200, +// lastUsedOn: { +// _nanoseconds: 61000000, +// _seconds: 1703615100, +// }, +// }, +// { +// id: '"mvWVuAxtSuhQtunjcywv"', +// date: { +// _seconds: 1684078062, +// _nanoseconds: 434000000, +// }, +// createdBy: 'k15z2SLFe1U2J3gshXUG', +// rolename: 'group-DSA-Coding-Group', +// roleid: '1107328395722899496', +// firstName: 'Test', +// lastName: 'User1', +// image: 'https://image.cdn.com/123dfgh', +// memberCount: 0, +// lastUsedOn: { +// _nanoseconds: 61070000, +// _seconds: 1703615154, +// }, +// }, +// ], +// }; const discordGroups = { message: 'Roles fetched successfully!', groups: [ @@ -17,43 +72,232 @@ const discordGroups = { }, { id: 'Mky71E6f6QWCY5MOBJFy', - date: { - _seconds: 1687619454, - _nanoseconds: 560000000, - }, - createdBy: 'jbGcfZLGYjHwxQ1Zh8ZJ', + date: { _seconds: 1687619454, _nanoseconds: 560000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', rolename: 'group-DSA', roleid: '1122182070509244416', firstName: 'Test', lastName: 'User2', image: 'https://image.cdn.com/12dfg', memberCount: 200, - lastUsedOn: { - _nanoseconds: 61000000, - _seconds: 1703615100, - }, + lastUsedOn: { _nanoseconds: 61000000, _seconds: 1703615100 }, }, { - id: '"mvWVuAxtSuhQtunjcywv"', - date: { - _seconds: 1684078062, - _nanoseconds: 434000000, - }, - createdBy: 'k15z2SLFe1U2J3gshXUG', + id: 'mvWVuAxtSuhQtunjcywv', + date: { _seconds: 1684078062, _nanoseconds: 434000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', rolename: 'group-DSA-Coding-Group', roleid: '1107328395722899496', firstName: 'Test', lastName: 'User1', image: 'https://image.cdn.com/123dfgh', memberCount: 0, - lastUsedOn: { - _nanoseconds: 61070000, - _seconds: 1703615154, - }, + lastUsedOn: { _nanoseconds: 61070000, _seconds: 1703615154 }, + }, + { + id: 'bqkJG7LaQsUbXIvK3dr4', + date: { _seconds: 1688239345, _nanoseconds: 230000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Frontend-Masters', + roleid: '1129834012345678910', + firstName: 'Test', + lastName: 'User3', + image: 'https://image.cdn.com/123dfr', + memberCount: 55, + }, + { + id: 'uHnmq7N9LRGQKXF2es5P', + date: { _seconds: 1684010000, _nanoseconds: 100000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Backend-Developers', + roleid: '1105328395722900496', + firstName: 'Test', + lastName: 'User4', + image: 'https://image.cdn.com/12ghy', + memberCount: 123, + }, + { + id: 'ZcwPKx6M9SLbCrLtMx8Q', + date: { _seconds: 1685021234, _nanoseconds: 320000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Python-Experts', + roleid: '1134568395722910496', + firstName: 'Test', + lastName: 'User5', + image: 'https://image.cdn.com/12dfxy', + memberCount: 78, + }, + { + id: 'k8HmdC4yT3VXwNLHtRz6', + date: { _seconds: 1684014567, _nanoseconds: 450000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-AI-Enthusiasts', + roleid: '1123418395722899235', + firstName: 'Test', + lastName: 'User6', + image: 'https://image.cdn.com/128dfh', + memberCount: 345, + }, + { + id: 'K9SLYx3T2YWHkNmCpX7P', + date: { _seconds: 1683018765, _nanoseconds: 470000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-JavaScript-Devs', + roleid: '1135768395722990956', + firstName: 'Test', + lastName: 'User7', + image: 'https://image.cdn.com/123xyz', + memberCount: 431, + }, + { + id: 'tR7GmM8qJ2VLxPcCtG5R', + date: { _seconds: 1683071453, _nanoseconds: 230000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-React-Devs', + roleid: '1123348395722890956', + firstName: 'Test', + lastName: 'User8', + image: 'https://image.cdn.com/126abc', + memberCount: 60, + }, + { + id: 'lKmBPv5Xw6SDkDnBfX4R', + date: { _seconds: 1682879831, _nanoseconds: 240000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-VueJS-Devs', + roleid: '1102348395722994956', + firstName: 'Test', + lastName: 'User9', + image: 'https://image.cdn.com/128ui', + memberCount: 33, + }, + { + id: 'X8BmLt5C7RDvVpNqSk3L', + date: { _seconds: 1683287654, _nanoseconds: 540000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-NodeJS-Masters', + roleid: '1134768395722911956', + firstName: 'Test', + lastName: 'User10', + image: 'https://image.cdn.com/123pqr', + memberCount: 502, + }, + { + id: 'G5QsWt2P6NDkCrLtFb3V', + date: { _seconds: 1682956789, _nanoseconds: 780000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-CSS-Wizards', + roleid: '1102398395722992956', + firstName: 'Test', + lastName: 'User11', + image: 'https://image.cdn.com/128dfg', + memberCount: 128, + }, + { + id: 'J2BmHt4L3RTgQwNpXk6M', + date: { _seconds: 1682234789, _nanoseconds: 870000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-C-Programming', + roleid: '1123568395722992956', + firstName: 'Test', + lastName: 'User12', + image: 'https://image.cdn.com/125abc', + memberCount: 205, + }, + { + id: 'S7NmKp5H6YDLtPrRtK4W', + date: { _seconds: 1682434567, _nanoseconds: 130000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Go-Language', + roleid: '1134998395722990956', + firstName: 'Test', + lastName: 'User13', + image: 'https://image.cdn.com/129def', + memberCount: 412, + }, + { + id: 'W3RvNx6P2VDqLvXtYf2J', + date: { _seconds: 1682703124, _nanoseconds: 190000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-TypeScript-Fans', + roleid: '1104768395722990056', + firstName: 'Test', + lastName: 'User14', + image: 'https://image.cdn.com/123uvw', + memberCount: 195, + }, + { + id: 'L9PlWt7K8VFnLpRkXh5Q', + date: { _seconds: 1683045123, _nanoseconds: 390000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-PHP-Devs', + roleid: '1134778395722993956', + firstName: 'Test', + lastName: 'User15', + image: 'https://image.cdn.com/123dfh', + memberCount: 251, + }, + { + id: 'Q4CmNt3P6RDnJqVxSk9L', + date: { _seconds: 1682512545, _nanoseconds: 630000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-AWS-Lovers', + roleid: '1107348395722994956', + firstName: 'Test', + lastName: 'User16', + image: 'https://image.cdn.com/124tyu', + memberCount: 332, + }, + { + id: 'M8DmCt8N6VFtVpNwZr2X', + date: { _seconds: 1682419999, _nanoseconds: 710000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Docker-Experts', + roleid: '1125678395722911056', + firstName: 'Test', + lastName: 'User17', + image: 'https://image.cdn.com/128ghj', + memberCount: 40, + }, + { + id: 'P2RlUt4K7YDnXpQkWv5M', + date: { _seconds: 1682654789, _nanoseconds: 230000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Kubernetes-Lovers', + roleid: '1134788395722990056', + firstName: 'Test', + lastName: 'User18', + image: 'https://image.cdn.com/123kly', + memberCount: 84, + }, + { + id: 'D5NmPt6V2RFoKvGxTr3V', + date: { _seconds: 1682901234, _nanoseconds: 430000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-Azure-Fans', + roleid: '1103458395722995956', + firstName: 'Test', + lastName: 'User19', + image: 'https://image.cdn.com/126nqr', + memberCount: 25, + }, + { + id: 'V6JkRp7P3MDkLvStXr7L', + date: { _seconds: 1682323456, _nanoseconds: 890000000 }, + createdBy: 'V4rqL1aDecNGoa1IxiCu', + rolename: 'group-GCP-Devs', + roleid: '1124798395722994956', + firstName: 'Test', + lastName: 'User20', + image: 'https://image.cdn.com/127dfg', + memberCount: 89, }, ], }; +// console.log(discordGroups); + + const GroupRoleData = { message: 'User group roles Id fetched successfully!', userId: '1234398439439989', From 440c74c99354f76de86166332739701349e77494 Mon Sep 17 00:00:00 2001 From: sailingsam Date: Wed, 25 Sep 2024 22:43:40 +0530 Subject: [PATCH 4/5] feat: lazyLoadDiscordRolesPage | Solved prettier fix --- __tests__/groups/group.test.js | 36 ++++++++++++++++++++++------------ groups/script.js | 14 ++++++------- groups/utils.js | 15 ++++++++------ mock-data/groups/index.js | 1 - 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/__tests__/groups/group.test.js b/__tests__/groups/group.test.js index dde3e11e..189ae93d 100644 --- a/__tests__/groups/group.test.js +++ b/__tests__/groups/group.test.js @@ -238,19 +238,26 @@ describe('Discord Groups Page', () => { test('Should load more groups on scroll', async () => { await page.goto(`${PAGE_URL}/groups`); await page.waitForSelector('.card', { timeout: 5000 }); - - const initialGroupCount = await page.$$eval('.card', (cards) => cards.length); - + + const initialGroupCount = await page.$$eval( + '.card', + (cards) => cards.length, + ); + await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); }); - await page.waitForFunction((initialCount) => { - return document.querySelectorAll('.card').length > initialCount; - }, {}, initialGroupCount); + await page.waitForFunction( + (initialCount) => { + return document.querySelectorAll('.card').length > initialCount; + }, + {}, + initialGroupCount, + ); const newGroupCount = await page.$$eval('.card', (cards) => cards.length); - + expect(newGroupCount).toBeGreaterThan(initialGroupCount); }); @@ -267,15 +274,18 @@ describe('Discord Groups Page', () => { } const finalGroupCount = await page.$$eval('.card', (cards) => cards.length); - + // Scroll one more time await page.evaluate(() => { window.scrollTo(0, document.body.scrollHeight); }); await page.waitForTimeout(1000); - const newFinalGroupCount = await page.$$eval('.card', (cards) => cards.length); - + const newFinalGroupCount = await page.$$eval( + '.card', + (cards) => cards.length, + ); + expect(newFinalGroupCount).toBe(finalGroupCount); }); @@ -306,7 +316,9 @@ describe('Discord Groups Page', () => { // Helper function to simulate paginated data function getPaginatedGroups(latestDoc) { const pageSize = 18; - const startIndex = latestDoc ? discordGroups.groups.findIndex(g => g.id === latestDoc) + 1 : 0; + const startIndex = latestDoc + ? discordGroups.groups.findIndex((g) => g.id === latestDoc) + 1 + : 0; const endIndex = startIndex + pageSize; const groups = discordGroups.groups.slice(startIndex, endIndex); const newLatestDoc = groups.length > 0 ? groups[groups.length - 1].id : null; @@ -316,4 +328,4 @@ function getPaginatedGroups(latestDoc) { groups, newLatestDoc, }; -} \ No newline at end of file +} diff --git a/groups/script.js b/groups/script.js index 4dbcbfa3..6e54f3ab 100644 --- a/groups/script.js +++ b/groups/script.js @@ -206,20 +206,20 @@ const afterAuthentication = async () => { }; const loadMoreGroups = async () => { if (dataStore.isLoading || !dataStore.hasMoreGroups) return; - + dataStore.isLoading = true; renderLoadingCards(); - + const newGroups = await getPaginatedDiscordGroups(); - + removeLoadingCards(); dataStore.isLoading = false; - + if (newGroups.length === 0) { dataStore.hasMoreGroups = false; return; } - + dataStore.groups = { ...dataStore.groups, ...newGroups.reduce((acc, group) => { @@ -239,12 +239,12 @@ const loadMoreGroups = async () => { return acc; }, {}), }; - + dataStore.filteredGroupsIds = [ ...dataStore.filteredGroupsIds, ...newGroups.map((group) => group.id), ]; - + return newGroups; }; diff --git a/groups/utils.js b/groups/utils.js index cb84a5eb..5702438f 100644 --- a/groups/utils.js +++ b/groups/utils.js @@ -47,13 +47,16 @@ async function getUserGroupRoles() { let latestDoc = 0; async function getPaginatedDiscordGroups() { try { - const res = await fetch(`${BASE_URL}/discord-actions/groups?latestDoc=${latestDoc}`, { - method: 'GET', - credentials: 'include', - headers: { - 'Content-type': 'application/json', + const res = await fetch( + `${BASE_URL}/discord-actions/groups?latestDoc=${latestDoc}`, + { + method: 'GET', + credentials: 'include', + headers: { + 'Content-type': 'application/json', + }, }, - }); + ); const { groups, newLatestDoc } = await res.json(); latestDoc = newLatestDoc; diff --git a/mock-data/groups/index.js b/mock-data/groups/index.js index 388bb7e1..6b876f12 100644 --- a/mock-data/groups/index.js +++ b/mock-data/groups/index.js @@ -297,7 +297,6 @@ const discordGroups = { // console.log(discordGroups); - const GroupRoleData = { message: 'User group roles Id fetched successfully!', userId: '1234398439439989', From a8011319141107a15da76067706d863dfc443293 Mon Sep 17 00:00:00 2001 From: sailingsam Date: Sat, 28 Sep 2024 02:35:20 +0530 Subject: [PATCH 5/5] feat: added feature flags --- __tests__/groups/group.test.js | 8 +-- groups/script.js | 97 +++++++++++++++++++++++----------- groups/utils.js | 20 ++++++- mock-data/groups/index.js | 1 + 4 files changed, 89 insertions(+), 37 deletions(-) diff --git a/__tests__/groups/group.test.js b/__tests__/groups/group.test.js index 189ae93d..3825e4b4 100644 --- a/__tests__/groups/group.test.js +++ b/__tests__/groups/group.test.js @@ -8,7 +8,7 @@ const PAGE_URL = 'http://localhost:8000'; describe('Discord Groups Page', () => { let browser; let page; - jest.setTimeout(60000); + jest.setTimeout(120000); beforeAll(async () => { browser = await puppeteer.launch({ @@ -237,7 +237,7 @@ describe('Discord Groups Page', () => { test('Should load more groups on scroll', async () => { await page.goto(`${PAGE_URL}/groups`); - await page.waitForSelector('.card', { timeout: 5000 }); + await page.waitForSelector('.card', { timeout: 10000 }); const initialGroupCount = await page.$$eval( '.card', @@ -252,14 +252,14 @@ describe('Discord Groups Page', () => { (initialCount) => { return document.querySelectorAll('.card').length > initialCount; }, - {}, + { timeout: 60000 }, initialGroupCount, ); const newGroupCount = await page.$$eval('.card', (cards) => cards.length); expect(newGroupCount).toBeGreaterThan(initialGroupCount); - }); + }, 120000); test('Should stop loading more groups when all groups are loaded', async () => { await page.goto(`${PAGE_URL}/groups`); diff --git a/groups/script.js b/groups/script.js index 6e54f3ab..d3140a67 100644 --- a/groups/script.js +++ b/groups/script.js @@ -15,6 +15,7 @@ import { import { addGroupRoleToMember, createDiscordGroupRole, + getDiscordGroups, getPaginatedDiscordGroups, getUserGroupRoles, getUserSelf, @@ -125,7 +126,7 @@ const dataStore = new Proxy( { userSelf: null, groups: null, - filteredGroupsIds: [], + filteredGroupsIds: isDev ? [] : null, search: isDev ? getParamValueFromURL(QUERY_PARAM_KEY.GROUP_SEARCH) : '', discordId: null, isGroupCreationModalOpen: false, @@ -151,7 +152,7 @@ const onCreate = () => { throw new Error(data); } dataStore.userSelf = data; - removeLoadingCards(); + isDev ? removeLoadingCards() : null; removeLoadingNavbarProfile(); await afterAuthentication(); }) @@ -170,39 +171,71 @@ const onCreate = () => { bindSearchInput(); bindSearchFocus(); bindGroupCreationButton(); - bindInfiniteScroll(); + isDev ? bindInfiniteScroll() : null; }; const afterAuthentication = async () => { renderNavbarProfile({ profile: dataStore.userSelf }); - await Promise.all([loadMoreGroups(), getUserGroupRoles()]).then( - ([groups, roleData]) => { - dataStore.filteredGroupsIds = groups.map((group) => group.id); - dataStore.groups = groups.reduce((acc, group) => { - let title = group.rolename - .replace('group-', '') - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - acc[group.id] = { - id: group.id, - title: title, - count: group.memberCount, - isMember: group.isMember, - roleId: group.roleid, - description: group.description, - isUpdating: false, - }; - return acc; - }, {}); - if (isDev) { - dataStore.filteredGroupsIds = getDiscordGroupIdsFromSearch( - Object.values(dataStore.groups), - dataStore.search, - ); - } - dataStore.discordId = roleData.userId; - }, - ); + if (isDev) { + await Promise.all([loadMoreGroups(), getUserGroupRoles()]).then( + ([groups, roleData]) => { + dataStore.filteredGroupsIds = groups.map((group) => group.id); + dataStore.groups = groups.reduce((acc, group) => { + let title = group.rolename + .replace('group-', '') + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + acc[group.id] = { + id: group.id, + title: title, + count: group.memberCount, + isMember: group.isMember, + roleId: group.roleid, + description: group.description, + isUpdating: false, + }; + return acc; + }, {}); + if (isDev) { + dataStore.filteredGroupsIds = getDiscordGroupIdsFromSearch( + Object.values(dataStore.groups), + dataStore.search, + ); + } + dataStore.discordId = roleData.userId; + }, + ); + } else { + await Promise.all([getDiscordGroups(), getUserGroupRoles()]).then( + ([groups, roleData]) => { + dataStore.filteredGroupsIds = groups.map((group) => group.id); + dataStore.groups = groups.reduce((acc, group) => { + let title = group.rolename + .replace('group-', '') + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + acc[group.id] = { + id: group.id, + title: title, + count: group.memberCount, + isMember: group.isMember, + roleId: group.roleid, + description: group.description, + isUpdating: false, + }; + return acc; + }, {}); + if (isDev) { + dataStore.filteredGroupsIds = getDiscordGroupIdsFromSearch( + Object.values(dataStore.groups), + dataStore.search, + ); + } + dataStore.discordId = roleData.userId; + }, + ); + } }; const loadMoreGroups = async () => { if (dataStore.isLoading || !dataStore.hasMoreGroups) return; diff --git a/groups/utils.js b/groups/utils.js index 5702438f..dd5c0658 100644 --- a/groups/utils.js +++ b/groups/utils.js @@ -44,11 +44,28 @@ async function getUserGroupRoles() { return await res.json(); } +async function getDiscordGroups() { + try { + const res = await fetch(`${BASE_URL}/discord-actions/groups`, { + method: 'GET', + credentials: 'include', + headers: { + 'Content-type': 'application/json', + }, + }); + + const { groups } = await res.json(); + return groups; + } catch (err) { + return err; + } +} + let latestDoc = 0; async function getPaginatedDiscordGroups() { try { const res = await fetch( - `${BASE_URL}/discord-actions/groups?latestDoc=${latestDoc}`, + `${BASE_URL}/discord-actions/groups?latestDoc=${latestDoc}&dev=true`, { method: 'GET', credentials: 'include', @@ -166,6 +183,7 @@ export { getUserGroupRoles, getMembers, getUserSelf, + getDiscordGroups, getPaginatedDiscordGroups, createDiscordGroupRole, addGroupRoleToMember, diff --git a/mock-data/groups/index.js b/mock-data/groups/index.js index 6b876f12..d6ac5f80 100644 --- a/mock-data/groups/index.js +++ b/mock-data/groups/index.js @@ -55,6 +55,7 @@ // }; const discordGroups = { message: 'Roles fetched successfully!', + latestDoc: '1124798395722994956', groups: [ { id: 'CqnEhbwtCqdcZdlrixLn',