From 09ebbf12be1aae4ea99a72c0bae511fce711368b Mon Sep 17 00:00:00 2001 From: sugh01 <19183308+sugh01@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:22:06 +0200 Subject: [PATCH 01/13] add grace period tests --- e2e/specs/stateless/gracePeriodNames.spec.ts | 146 +++++++++++++++++++ playwright.config.ts | 2 +- playwright/pageObjects/index.ts | 2 + playwright/pageObjects/myNamesPage.ts | 26 ++++ 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 e2e/specs/stateless/gracePeriodNames.spec.ts create mode 100644 playwright/pageObjects/myNamesPage.ts diff --git a/e2e/specs/stateless/gracePeriodNames.spec.ts b/e2e/specs/stateless/gracePeriodNames.spec.ts new file mode 100644 index 000000000..6af28de18 --- /dev/null +++ b/e2e/specs/stateless/gracePeriodNames.spec.ts @@ -0,0 +1,146 @@ +/* eslint-disable no-await-in-loop */ +import { expect } from '@playwright/test' + +import { test } from '../../../playwright' + +test('owner test', async ({ page, + login, + makePageObject, + makeName }) => { + //make 200 names + const name = await makeName({ + label: 'legacy', + type: 'legacy', + owner: 'user', + duration: -24 * 60 * 60, + }) + + const profilePage = makePageObject('ProfilePage') + await profilePage.goto(name) + const namesPage = makePageObject('MyNamesPage') + await login.connect() + await namesPage.goto() + await expect(page.getByText(`${name}`)).toBeVisible({ + timeout: 15000, + }) + + const namesList = [] + + for (let i = 0; i < 50; i++) { + namesList.push( + await makeName({ + label: `legacy-${i}`, + type: 'legacy', + owner: 'user', + duration: -24 * 60 * 60, + })) + } + await namesPage.goto() + await namesPage.searchField.fill(namesList[19]) + await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ + timeout: 15000, + }) + await namesPage.goto() + await namesPage.scrollToEnd() + await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ + timeout: 15000, + }) +}) + +test('manager test', async ({ page, + login, + accounts, + provider, + time, + makePageObject, + makeName }) => { + //make 200 names + const name = await makeName({ + label: 'legacy', + type: 'legacy', + owner: 'user2', + manager: 'user', + duration: -24 * 60 * 60, + }) + + const profilePage = makePageObject('ProfilePage') + await profilePage.goto(name) + const namesPage = makePageObject('MyNamesPage') + await login.connect() + await namesPage.goto() + await expect(page.getByText(`${name}`)).toBeVisible({ + timeout: 15000, + }) + + const namesList = [] + + for (let i = 0; i < 50; i++) { + namesList.push( + await makeName({ + label: `legacy-${i}`, + type: 'legacy', + owner: 'user2', + manager: 'user', + duration: -24 * 60 * 60, + })) + } + await namesPage.goto() + await namesPage.searchField.fill(namesList[19]) + await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ + timeout: 15000, + }) + await namesPage.goto() + await namesPage.scrollToEnd() + await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ + timeout: 15000, + }) +}) + +test('manager test expires', async ({ page, + login, + accounts, + provider, + time, + makePageObject, + makeName }) => { + //make 200 names + const name = await makeName({ + label: 'legacy', + type: 'legacy', + owner: 'user2', + manager: 'user', + duration: 24 * 60 * 60, + }) + + const profilePage = makePageObject('ProfilePage') + await profilePage.goto(name) + const namesPage = makePageObject('MyNamesPage') + await login.connect() + await namesPage.goto() + await expect(page.getByText(`${name}`)).toBeVisible({ + timeout: 15000, + }) + + const namesList = [] + + for (let i = 0; i < 50; i++) { + namesList.push( + await makeName({ + label: `legacy-${i}`, + type: 'legacy', + owner: 'user2', + manager: 'user', + duration: 48 * 60 * 60, + })) + } + await namesPage.goto() + await namesPage.searchField.fill(namesList[19]) + await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ + timeout: 15000, + }) + await namesPage.goto() + await namesPage.scrollToEnd() + await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ + timeout: 15000, + }) +}) diff --git a/playwright.config.ts b/playwright.config.ts index 059e39758..18ed9ceb7 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ testDir: './e2e/specs', testMatch: '*.spec.ts', retries: process.env.CI ? 2 : 0, - timeout: 120000, // add extra time for loading + timeout: 12000000, // add extra time for loading fullyParallel: true, // required to evenly shard workers: 1, // keep tests serial for now reporter: [['html', { open: 'always' }]], diff --git a/playwright/pageObjects/index.ts b/playwright/pageObjects/index.ts index 70db147d0..4ce371a00 100644 --- a/playwright/pageObjects/index.ts +++ b/playwright/pageObjects/index.ts @@ -17,6 +17,7 @@ import { RegistrationPage } from './registrationPage' import { SendNameModal } from './sendNameModal' import { SubnamesPage } from './subnamePage' import { TransactionModal } from './transactionModal' +import {MyNamesPage} from './myNamesPage' type Dependencies = { page: Page; wallet: Web3ProviderBackend } @@ -36,6 +37,7 @@ const pageObjects = { TransactionModal, RecordsPage, AdvancedEditorModal, + MyNamesPage } type PageObjects = typeof pageObjects diff --git a/playwright/pageObjects/myNamesPage.ts b/playwright/pageObjects/myNamesPage.ts new file mode 100644 index 000000000..404d036b6 --- /dev/null +++ b/playwright/pageObjects/myNamesPage.ts @@ -0,0 +1,26 @@ +/* eslint-disable import/no-extraneous-dependencies */ + +/* eslint-disable no-await-in-loop */ +import { Locator, Page } from '@playwright/test' + +export class MyNamesPage { + readonly page: Page + + readonly searchField: Locator + + constructor(page: Page) { + this.page = page + this.searchField = this.page.getByTestId('name-table-header-search') + } + + async goto() { + await this.page.goto(`/my/names`) + } + + async scrollToEnd() { + await this.page.evaluate(() => { + window.scrollTo(0, document.body.scrollHeight) + }) + } + +} From 2b0747bceb4e1c4c34036f443fca09a0d095c629 Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Thu, 18 Jul 2024 09:58:48 +0800 Subject: [PATCH 02/13] wip --- src/hooks/useProfileActions.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hooks/useProfileActions.ts b/src/hooks/useProfileActions.ts index 3611e509e..1eeb90a63 100644 --- a/src/hooks/useProfileActions.ts +++ b/src/hooks/useProfileActions.ts @@ -39,6 +39,8 @@ type Props = { enabled?: boolean } +const editButtonTooltip = () => {} + export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => { const { t } = useTranslation('profile') const { createTransactionFlow, usePreparedDataInput } = useTransactionFlow() @@ -140,7 +142,12 @@ export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => }) } - if (abilities.canEdit && (abilities.canEditRecords || abilities.canEditResolver)) { + // if (abilities.canEdit && (abilities.canEditRecords || abilities.canEditResolver)) { + // } + console.log('ownerData: ', ownerData) + console.log('wrapperData: ', wrapperData) + const isOwnerOrManager = address === ownerData?.owner || ownerData?.registrant === address + if (isOwnerOrManager) { actions.push({ label: t('tabs.profile.actions.editProfile.label'), tooltipContent: hasGraphError From 82fb73d645fbd208238196f7d85456f82650744d Mon Sep 17 00:00:00 2001 From: Yee Fung Date: Wed, 17 Jul 2024 21:23:34 -0500 Subject: [PATCH 03/13] added additional logic for the edit button --- public/locales/en/profile.json | 4 ++-- src/hooks/useProfileActions.ts | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json index 12636fbce..a14c83f62 100644 --- a/public/locales/en/profile.json +++ b/public/locales/en/profile.json @@ -71,7 +71,6 @@ "grace-period": { "title": "Grace period ends", "tooltip": "A 90 day grace window after expiration, when the name can still be extended but not re-registered." - }, "registration": { "title": "Registered" @@ -452,6 +451,7 @@ "permissionRevoked": "This name has revoked the permissions needed to perform this action.", "gracePeriod": "This cannot be done because the name has expired", "default": "This action is not available", - "invalidJSON": "Invalid JSON" + "invalidJSON": "Invalid JSON", + "isOwnerCannotEdit": "You must be the manager in order to edit the profile" } } diff --git a/src/hooks/useProfileActions.ts b/src/hooks/useProfileActions.ts index 1eeb90a63..2d5bbf0f1 100644 --- a/src/hooks/useProfileActions.ts +++ b/src/hooks/useProfileActions.ts @@ -39,7 +39,16 @@ type Props = { enabled?: boolean } -const editButtonTooltip = () => {} +const editButtonTooltip = (toolTipConditions: { + hasGraphError: boolean + canEdit: boolean + t: TFunction +}) => { + const { hasGraphError, canEdit, t } = toolTipConditions + if (hasGraphError) return t('errors.networkError.blurb', { ns: 'common' }) + if (!canEdit) return t('errors.isOwnerCannotEdit') + return undefined +} export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => { const { t } = useTranslation('profile') @@ -144,15 +153,11 @@ export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => // if (abilities.canEdit && (abilities.canEditRecords || abilities.canEditResolver)) { // } - console.log('ownerData: ', ownerData) - console.log('wrapperData: ', wrapperData) const isOwnerOrManager = address === ownerData?.owner || ownerData?.registrant === address if (isOwnerOrManager) { actions.push({ label: t('tabs.profile.actions.editProfile.label'), - tooltipContent: hasGraphError - ? t('errors.networkError.blurb', { ns: 'common' }) - : undefined, + tooltipContent: editButtonTooltip({ hasGraphError, canEdit: abilities.canEdit }), tooltipPlacement: 'left', loading: hasGraphErrorLoading, onClick: () => From 47c191468c2bffb5006a153ce3789d37df2eeb91 Mon Sep 17 00:00:00 2001 From: storywithoutend Date: Thu, 18 Jul 2024 11:27:36 +0800 Subject: [PATCH 04/13] add additional conditions getEditTooltipContent --- public/locales/en/profile.json | 3 ++- src/hooks/useProfileActions.test.ts | 39 +++++++++++++++++++++++++++++ src/hooks/useProfileActions.ts | 24 +++++++++++------- vitest.config.mts | 4 ++- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json index a14c83f62..7da1f4f71 100644 --- a/public/locales/en/profile.json +++ b/public/locales/en/profile.json @@ -452,6 +452,7 @@ "gracePeriod": "This cannot be done because the name has expired", "default": "This action is not available", "invalidJSON": "Invalid JSON", - "isOwnerCannotEdit": "You must be the manager in order to edit the profile" + "isOwnerCannotEdit": "You must be the manager in order to edit the profile", + "cannotEdit": "You do not have permission to update to an authorised resolver" } } diff --git a/src/hooks/useProfileActions.test.ts b/src/hooks/useProfileActions.test.ts index b878d1f96..52234644e 100644 --- a/src/hooks/useProfileActions.test.ts +++ b/src/hooks/useProfileActions.test.ts @@ -20,6 +20,7 @@ import { useGetPrimaryNameTransactionFlowItem } from './primary/useGetPrimaryNam import { useResolverStatus } from './resolver/useResolverStatus' import { useProfile } from './useProfile' import { useProfileActions } from './useProfileActions' +import { makeMockUseAbilitiesData } from '@root/test/mock/makeMockUseAbilitiesData' const NOW_TIMESTAMP = 1588994800000 vi.spyOn(Date, 'now').mockImplementation(() => NOW_TIMESTAMP) @@ -538,4 +539,42 @@ describe('useProfileActions', () => { ).toEqual(1) }) }) + + describe.only('edit profile button', () => { + it('Should show edit profile button if user is manager', () => { + + }) + + it('Should show disabled profile button if there is a graph error', () => { + + }) + it('Should show disabled edit profile button if user is owner but not manager', () => { + mockUseAbilities.mockReturnValue({ + data: makeMockUseAbilitiesData('eth-unwrapped-2ld:owner'), + isLoading: false + }) + + const { result} = renderHook(() => useProfileActions({name: 'test.eth'})) + expect(result.current.profileActions).toEqual(expect.arrayContaining([expect.objectContaining({ + label: "tabs.profile.actions.editProfile.label", + tooltipContent: "errors.isOwnerCannotEdit" + })])) + }) + + it('Should show disabled edit profile button if name is wrapped but fuse for edit resolver is burned and resolver is unauthorised', () => { + mockUseAbilities.mockReturnValue({ + data: { + ...makeMockUseAbilitiesData('eth-burnt-2ld'), + canEditRecords: false + }, + isLoading: false + }) + + const { result} = renderHook(() => useProfileActions({name: 'test.eth'})) + expect(result.current.profileActions).toEqual(expect.arrayContaining([expect.objectContaining({ + label: "tabs.profile.actions.editProfile.label", + tooltipContent: "errors.cannotEdit" + })])) + }) }) +}) \ No newline at end of file diff --git a/src/hooks/useProfileActions.ts b/src/hooks/useProfileActions.ts index 2d5bbf0f1..dc5d5525e 100644 --- a/src/hooks/useProfileActions.ts +++ b/src/hooks/useProfileActions.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import { useTranslation } from 'react-i18next' +import { TFunction, useTranslation } from 'react-i18next' import { checkIsDecrypted } from '@ensdomains/ensjs/utils' @@ -11,7 +11,7 @@ import { checkAvailablePrimaryName } from '@app/utils/checkAvailablePrimaryName' import { nameParts } from '@app/utils/name' import { useHasGraphError } from '@app/utils/SyncProvider/SyncProvider' -import { useAbilities } from './abilities/useAbilities' +import { Abilities, useAbilities } from './abilities/useAbilities' import { useAccountSafely } from './account/useAccountSafely' import { useExpiry } from './ensjs/public/useExpiry' import { useOwner } from './ensjs/public/useOwner' @@ -39,14 +39,18 @@ type Props = { enabled?: boolean } -const editButtonTooltip = (toolTipConditions: { +const editButtonTooltip = ({ + hasGraphError, + abilities, + t, +}: { hasGraphError: boolean - canEdit: boolean + abilities: Abilities t: TFunction }) => { - const { hasGraphError, canEdit, t } = toolTipConditions if (hasGraphError) return t('errors.networkError.blurb', { ns: 'common' }) - if (!canEdit) return t('errors.isOwnerCannotEdit') + if (!abilities.canEdit) return t('errors.isOwnerCannotEdit') + if (!abilities.canEditRecords && !abilities.canEditResolver) return t('errors.cannotEdit') return undefined } @@ -151,13 +155,15 @@ export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => }) } - // if (abilities.canEdit && (abilities.canEditRecords || abilities.canEditResolver)) { - // } const isOwnerOrManager = address === ownerData?.owner || ownerData?.registrant === address if (isOwnerOrManager) { actions.push({ label: t('tabs.profile.actions.editProfile.label'), - tooltipContent: editButtonTooltip({ hasGraphError, canEdit: abilities.canEdit }), + tooltipContent: editButtonTooltip({ + hasGraphError: !!hasGraphError, + abilities, + t, + }), tooltipPlacement: 'left', loading: hasGraphErrorLoading, onClick: () => diff --git a/vitest.config.mts b/vitest.config.mts index a882247c8..0578682bb 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -21,8 +21,10 @@ export default defineConfig({ globals: false, environment: 'jsdom', alias: { - // eslint-disable-next-line @typescript-eslint/naming-convention + /* eslint-disable @typescript-eslint/naming-convention */ '@app/': new URL('./src/', import.meta.url).pathname, + '@root/': new URL('./', import.meta.url).pathname, + /* eslint-enable @typescript-eslint/naming-convention */ }, include: ['src/**/*.test.ts', 'src/**/*.test.tsx'], setupFiles: ['./test/textencoder-setup.mts', './test/websocket-setup.mts'], From 060692dffd0ca56890a97c945e05fab9136bb9dd Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Thu, 18 Jul 2024 22:50:34 +0700 Subject: [PATCH 05/13] Add test cases for 'Should show edit profile button if user is manager' and 'Should show disabled profile button if there is a graph error' --- src/hooks/useProfileActions.test.ts | 69 ++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/src/hooks/useProfileActions.test.ts b/src/hooks/useProfileActions.test.ts index 52234644e..cc82e21b6 100644 --- a/src/hooks/useProfileActions.test.ts +++ b/src/hooks/useProfileActions.test.ts @@ -1,5 +1,6 @@ import { mockFunction, renderHook, waitFor } from '@app/test-utils' +import { makeMockUseAbilitiesData } from '@root/test/mock/makeMockUseAbilitiesData' import { labelhash } from 'viem' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' @@ -20,7 +21,6 @@ import { useGetPrimaryNameTransactionFlowItem } from './primary/useGetPrimaryNam import { useResolverStatus } from './resolver/useResolverStatus' import { useProfile } from './useProfile' import { useProfileActions } from './useProfileActions' -import { makeMockUseAbilitiesData } from '@root/test/mock/makeMockUseAbilitiesData' const NOW_TIMESTAMP = 1588994800000 vi.spyOn(Date, 'now').mockImplementation(() => NOW_TIMESTAMP) @@ -542,39 +542,74 @@ describe('useProfileActions', () => { describe.only('edit profile button', () => { it('Should show edit profile button if user is manager', () => { - + mockUseAbilities.mockReturnValue({ + data: makeMockUseAbilitiesData('eth-unwrapped-2ld:manager'), + isLoading: false, + }) + + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) + expect(result.current.profileActions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + label: 'tabs.profile.actions.editProfile.label', + tooltipContent: undefined, + }), + ]), + ) }) it('Should show disabled profile button if there is a graph error', () => { - + mockUseHasGraphError.mockReturnValue({ + data: true, + isLoading: false, + }) + + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) + expect(result.current.profileActions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + label: 'tabs.profile.actions.editProfile.label', + tooltipContent: 'errors.networkError.blurb', + }), + ]), + ) }) + it('Should show disabled edit profile button if user is owner but not manager', () => { mockUseAbilities.mockReturnValue({ data: makeMockUseAbilitiesData('eth-unwrapped-2ld:owner'), - isLoading: false + isLoading: false, }) - const { result} = renderHook(() => useProfileActions({name: 'test.eth'})) - expect(result.current.profileActions).toEqual(expect.arrayContaining([expect.objectContaining({ - label: "tabs.profile.actions.editProfile.label", - tooltipContent: "errors.isOwnerCannotEdit" - })])) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) + expect(result.current.profileActions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + label: 'tabs.profile.actions.editProfile.label', + tooltipContent: 'errors.isOwnerCannotEdit', + }), + ]), + ) }) it('Should show disabled edit profile button if name is wrapped but fuse for edit resolver is burned and resolver is unauthorised', () => { mockUseAbilities.mockReturnValue({ data: { ...makeMockUseAbilitiesData('eth-burnt-2ld'), - canEditRecords: false + canEditRecords: false, }, - isLoading: false + isLoading: false, }) - const { result} = renderHook(() => useProfileActions({name: 'test.eth'})) - expect(result.current.profileActions).toEqual(expect.arrayContaining([expect.objectContaining({ - label: "tabs.profile.actions.editProfile.label", - tooltipContent: "errors.cannotEdit" - })])) + const { result } = renderHook(() => useProfileActions({ name: 'test.eth' })) + expect(result.current.profileActions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + label: 'tabs.profile.actions.editProfile.label', + tooltipContent: 'errors.cannotEdit', + }), + ]), + ) }) + }) }) -}) \ No newline at end of file From 1bf5be41543ac1ac89d753e16aab83ac2963de1c Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Thu, 18 Jul 2024 23:03:54 +0700 Subject: [PATCH 06/13] Remove .only from test describing edit profile button - test was failing --- src/hooks/useProfileActions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useProfileActions.test.ts b/src/hooks/useProfileActions.test.ts index cc82e21b6..e34a749a2 100644 --- a/src/hooks/useProfileActions.test.ts +++ b/src/hooks/useProfileActions.test.ts @@ -540,7 +540,7 @@ describe('useProfileActions', () => { }) }) - describe.only('edit profile button', () => { + describe('edit profile button', () => { it('Should show edit profile button if user is manager', () => { mockUseAbilities.mockReturnValue({ data: makeMockUseAbilitiesData('eth-unwrapped-2ld:manager'), From 8ef199a659fff8ab228691c1f939f33f0bb65b30 Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Fri, 19 Jul 2024 18:32:14 +0700 Subject: [PATCH 07/13] Fix ESLint error causing build failures on GitHub Actions --- e2e/specs/stateless/gracePeriodNames.spec.ts | 82 +++++++++----------- src/hooks/useProfileActions.ts | 14 +--- 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/e2e/specs/stateless/gracePeriodNames.spec.ts b/e2e/specs/stateless/gracePeriodNames.spec.ts index 6af28de18..7a533814e 100644 --- a/e2e/specs/stateless/gracePeriodNames.spec.ts +++ b/e2e/specs/stateless/gracePeriodNames.spec.ts @@ -3,18 +3,15 @@ import { expect } from '@playwright/test' import { test } from '../../../playwright' -test('owner test', async ({ page, - login, - makePageObject, - makeName }) => { - //make 200 names +test('owner test', async ({ page, login, makePageObject, makeName }) => { + // make 200 names const name = await makeName({ label: 'legacy', type: 'legacy', owner: 'user', duration: -24 * 60 * 60, }) - + const profilePage = makePageObject('ProfilePage') await profilePage.goto(name) const namesPage = makePageObject('MyNamesPage') @@ -26,14 +23,15 @@ test('owner test', async ({ page, const namesList = [] - for (let i = 0; i < 50; i++) { + for (let i = 0; i < 50; i += 1) { namesList.push( - await makeName({ - label: `legacy-${i}`, - type: 'legacy', - owner: 'user', - duration: -24 * 60 * 60, - })) + await makeName({ + label: `legacy-${i}`, + type: 'legacy', + owner: 'user', + duration: -24 * 60 * 60, + }), + ) } await namesPage.goto() await namesPage.searchField.fill(namesList[19]) @@ -47,14 +45,8 @@ test('owner test', async ({ page, }) }) -test('manager test', async ({ page, - login, - accounts, - provider, - time, - makePageObject, - makeName }) => { - //make 200 names +test('manager test', async ({ page, login, makePageObject, makeName }) => { + // make 200 names const name = await makeName({ label: 'legacy', type: 'legacy', @@ -62,7 +54,7 @@ test('manager test', async ({ page, manager: 'user', duration: -24 * 60 * 60, }) - + const profilePage = makePageObject('ProfilePage') await profilePage.goto(name) const namesPage = makePageObject('MyNamesPage') @@ -74,15 +66,16 @@ test('manager test', async ({ page, const namesList = [] - for (let i = 0; i < 50; i++) { + for (let i = 0; i < 50; i += 1) { namesList.push( - await makeName({ - label: `legacy-${i}`, - type: 'legacy', - owner: 'user2', - manager: 'user', - duration: -24 * 60 * 60, - })) + await makeName({ + label: `legacy-${i}`, + type: 'legacy', + owner: 'user2', + manager: 'user', + duration: -24 * 60 * 60, + }), + ) } await namesPage.goto() await namesPage.searchField.fill(namesList[19]) @@ -96,14 +89,8 @@ test('manager test', async ({ page, }) }) -test('manager test expires', async ({ page, - login, - accounts, - provider, - time, - makePageObject, - makeName }) => { - //make 200 names +test('manager test expires', async ({ page, login, makePageObject, makeName }) => { + // make 200 names const name = await makeName({ label: 'legacy', type: 'legacy', @@ -111,7 +98,7 @@ test('manager test expires', async ({ page, manager: 'user', duration: 24 * 60 * 60, }) - + const profilePage = makePageObject('ProfilePage') await profilePage.goto(name) const namesPage = makePageObject('MyNamesPage') @@ -123,15 +110,16 @@ test('manager test expires', async ({ page, const namesList = [] - for (let i = 0; i < 50; i++) { + for (let i = 0; i < 50; i += 1) { namesList.push( - await makeName({ - label: `legacy-${i}`, - type: 'legacy', - owner: 'user2', - manager: 'user', - duration: 48 * 60 * 60, - })) + await makeName({ + label: `legacy-${i}`, + type: 'legacy', + owner: 'user2', + manager: 'user', + duration: 48 * 60 * 60, + }), + ) } await namesPage.goto() await namesPage.searchField.fill(namesList[19]) diff --git a/src/hooks/useProfileActions.ts b/src/hooks/useProfileActions.ts index dc5d5525e..26195089c 100644 --- a/src/hooks/useProfileActions.ts +++ b/src/hooks/useProfileActions.ts @@ -289,17 +289,9 @@ export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => getPrimaryNameTransactionFlowItem, name, isAvailablePrimaryName, - abilities.canEdit, - abilities.canEditRecords, - abilities.canEditResolver, - abilities.canDelete, - abilities.canDeleteContract, - abilities.canDeleteError, - abilities.canReclaim, - abilities.canDeleteRequiresWrap, - abilities.isPCCBurned, - abilities.isParentOwner, - abilities.canDeleteMethod, + ownerData?.owner, + ownerData?.registrant, + abilities, t, hasGraphError, hasGraphErrorLoading, From c98f289f6e1609bf65e15a2714db70626d96540d Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Mon, 22 Jul 2024 09:06:52 +0800 Subject: [PATCH 08/13] remove uneeded files --- e2e/specs/stateless/gracePeriodNames.spec.ts | 134 ------------------- playwright/pageObjects/index.ts | 6 +- playwright/pageObjects/myNamesPage.ts | 26 ---- 3 files changed, 2 insertions(+), 164 deletions(-) delete mode 100644 e2e/specs/stateless/gracePeriodNames.spec.ts delete mode 100644 playwright/pageObjects/myNamesPage.ts diff --git a/e2e/specs/stateless/gracePeriodNames.spec.ts b/e2e/specs/stateless/gracePeriodNames.spec.ts deleted file mode 100644 index 7a533814e..000000000 --- a/e2e/specs/stateless/gracePeriodNames.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -/* eslint-disable no-await-in-loop */ -import { expect } from '@playwright/test' - -import { test } from '../../../playwright' - -test('owner test', async ({ page, login, makePageObject, makeName }) => { - // make 200 names - const name = await makeName({ - label: 'legacy', - type: 'legacy', - owner: 'user', - duration: -24 * 60 * 60, - }) - - const profilePage = makePageObject('ProfilePage') - await profilePage.goto(name) - const namesPage = makePageObject('MyNamesPage') - await login.connect() - await namesPage.goto() - await expect(page.getByText(`${name}`)).toBeVisible({ - timeout: 15000, - }) - - const namesList = [] - - for (let i = 0; i < 50; i += 1) { - namesList.push( - await makeName({ - label: `legacy-${i}`, - type: 'legacy', - owner: 'user', - duration: -24 * 60 * 60, - }), - ) - } - await namesPage.goto() - await namesPage.searchField.fill(namesList[19]) - await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ - timeout: 15000, - }) - await namesPage.goto() - await namesPage.scrollToEnd() - await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ - timeout: 15000, - }) -}) - -test('manager test', async ({ page, login, makePageObject, makeName }) => { - // make 200 names - const name = await makeName({ - label: 'legacy', - type: 'legacy', - owner: 'user2', - manager: 'user', - duration: -24 * 60 * 60, - }) - - const profilePage = makePageObject('ProfilePage') - await profilePage.goto(name) - const namesPage = makePageObject('MyNamesPage') - await login.connect() - await namesPage.goto() - await expect(page.getByText(`${name}`)).toBeVisible({ - timeout: 15000, - }) - - const namesList = [] - - for (let i = 0; i < 50; i += 1) { - namesList.push( - await makeName({ - label: `legacy-${i}`, - type: 'legacy', - owner: 'user2', - manager: 'user', - duration: -24 * 60 * 60, - }), - ) - } - await namesPage.goto() - await namesPage.searchField.fill(namesList[19]) - await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ - timeout: 15000, - }) - await namesPage.goto() - await namesPage.scrollToEnd() - await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ - timeout: 15000, - }) -}) - -test('manager test expires', async ({ page, login, makePageObject, makeName }) => { - // make 200 names - const name = await makeName({ - label: 'legacy', - type: 'legacy', - owner: 'user2', - manager: 'user', - duration: 24 * 60 * 60, - }) - - const profilePage = makePageObject('ProfilePage') - await profilePage.goto(name) - const namesPage = makePageObject('MyNamesPage') - await login.connect() - await namesPage.goto() - await expect(page.getByText(`${name}`)).toBeVisible({ - timeout: 15000, - }) - - const namesList = [] - - for (let i = 0; i < 50; i += 1) { - namesList.push( - await makeName({ - label: `legacy-${i}`, - type: 'legacy', - owner: 'user2', - manager: 'user', - duration: 48 * 60 * 60, - }), - ) - } - await namesPage.goto() - await namesPage.searchField.fill(namesList[19]) - await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ - timeout: 15000, - }) - await namesPage.goto() - await namesPage.scrollToEnd() - await expect(page.getByText(`${namesList[19]}`)).toBeVisible({ - timeout: 15000, - }) -}) diff --git a/playwright/pageObjects/index.ts b/playwright/pageObjects/index.ts index c6b9deb9b..153138202 100644 --- a/playwright/pageObjects/index.ts +++ b/playwright/pageObjects/index.ts @@ -14,12 +14,11 @@ import { PermissionsPage } from './permissionsPage' import { ProfilePage } from './profilePage' import { RecordsPage } from './recordsPage' import { RegistrationPage } from './registrationPage' +import { SelectPrimaryNameModal } from './selectPrimaryNameModal' import { SendNameModal } from './sendNameModal' -import {SettingsPage} from './settingsPage' -import {SelectPrimaryNameModal} from './selectPrimaryNameModal' +import { SettingsPage } from './settingsPage' import { SubnamesPage } from './subnamePage' import { TransactionModal } from './transactionModal' -import {MyNamesPage} from './myNamesPage' type Dependencies = { page: Page; wallet: Web3ProviderBackend } @@ -41,7 +40,6 @@ const pageObjects = { TransactionModal, RecordsPage, AdvancedEditorModal, - MyNamesPage } type PageObjects = typeof pageObjects diff --git a/playwright/pageObjects/myNamesPage.ts b/playwright/pageObjects/myNamesPage.ts deleted file mode 100644 index 404d036b6..000000000 --- a/playwright/pageObjects/myNamesPage.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable import/no-extraneous-dependencies */ - -/* eslint-disable no-await-in-loop */ -import { Locator, Page } from '@playwright/test' - -export class MyNamesPage { - readonly page: Page - - readonly searchField: Locator - - constructor(page: Page) { - this.page = page - this.searchField = this.page.getByTestId('name-table-header-search') - } - - async goto() { - await this.page.goto(`/my/names`) - } - - async scrollToEnd() { - await this.page.evaluate(() => { - window.scrollTo(0, document.body.scrollHeight) - }) - } - -} From fe3782b7519fa3c4132c0cdd6ae7100b7b01c435 Mon Sep 17 00:00:00 2001 From: Leon Talbert Date: Mon, 22 Jul 2024 09:08:32 +0800 Subject: [PATCH 09/13] Update playwright.config.ts --- playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright.config.ts b/playwright.config.ts index 18ed9ceb7..059e39758 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,7 +5,7 @@ export default defineConfig({ testDir: './e2e/specs', testMatch: '*.spec.ts', retries: process.env.CI ? 2 : 0, - timeout: 12000000, // add extra time for loading + timeout: 120000, // add extra time for loading fullyParallel: true, // required to evenly shard workers: 1, // keep tests serial for now reporter: [['html', { open: 'always' }]], From 9f74257cd87099c6226e4ad113233deaf7388544 Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Fri, 26 Jul 2024 20:45:52 +0700 Subject: [PATCH 10/13] Add e2e stateless for checking profile button in owner and manager --- e2e/specs/stateless/profileEditor.spec.ts | 68 +++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/e2e/specs/stateless/profileEditor.spec.ts b/e2e/specs/stateless/profileEditor.spec.ts index b84999320..80933b385 100644 --- a/e2e/specs/stateless/profileEditor.spec.ts +++ b/e2e/specs/stateless/profileEditor.spec.ts @@ -914,3 +914,71 @@ test.describe('subgraph errors', () => { await page.getByTestId('subgraph-network-error').uncheck() }) }) + +test('Should show edit profile button if user is manager', async ({ + login, + makeName, + makePageObject, +}) => { + const name = await makeName({ + label: 'unwrapped', + type: 'legacy', + owner: 'user', + manager: 'user', + }) + + const profilePage = makePageObject('ProfilePage') + + await profilePage.goto(name) + await login.connect() + + await expect(profilePage.editProfileButton).toBeVisible() +}) + +test('Should show disabled edit profile button if user is owner but not manager', async ({ + page, + login, + makeName, + makePageObject, +}) => { + const name = await makeName({ + label: 'unwrapped', + type: 'legacy', + owner: 'user', + manager: 'user2', + }) + + const profilePage = makePageObject('ProfilePage') + + await profilePage.goto(name) + await login.connect() + + await expect(page.getByTestId('disabled-profile-action-Edit profile')).toBeVisible() +}) + +// test('Should show disabled edit profile button if name is wrapped but use for edit resolver is burned and resolver is unauthorised', async ({ +// page, +// login, +// makeName, +// makePageObject, +// }) => { +// const name = await makeName({ +// label: 'wrapped', +// type: 'wrapped', +// owner: 'user', +// fuses: { +// named: ['CANNOT_UNWRAP', 'CANNOT_SET_RESOLVER'], +// }, +// }) + +// console.log('name', name) + +// const profilePage = makePageObject('ProfilePage') + +// await profilePage.goto(name) +// await login.connect() + +// await page.pause() + +// await expect(page.getByTestId('disabled-profile-action-Edit profile')).toBeVisible() +// }) From 12996af519f030cdbaffa284cc2b901ea142f1c9 Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Mon, 29 Jul 2024 18:09:02 +0700 Subject: [PATCH 11/13] Revert dependencies for useProfileActions to avoid unexpected rerender, update manager user to user2 for test case "Should show edit profile button if user is manager" --- e2e/specs/stateless/profileEditor.spec.ts | 5 ++--- src/hooks/useProfileActions.ts | 12 +++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/e2e/specs/stateless/profileEditor.spec.ts b/e2e/specs/stateless/profileEditor.spec.ts index 80933b385..2df5402e6 100644 --- a/e2e/specs/stateless/profileEditor.spec.ts +++ b/e2e/specs/stateless/profileEditor.spec.ts @@ -924,7 +924,7 @@ test('Should show edit profile button if user is manager', async ({ label: 'unwrapped', type: 'legacy', owner: 'user', - manager: 'user', + manager: 'user2', }) const profilePage = makePageObject('ProfilePage') @@ -966,13 +966,12 @@ test('Should show disabled edit profile button if user is owner but not manager' // label: 'wrapped', // type: 'wrapped', // owner: 'user', +// resolver: ownedResolverAddress as Address, // fuses: { // named: ['CANNOT_UNWRAP', 'CANNOT_SET_RESOLVER'], // }, // }) -// console.log('name', name) - // const profilePage = makePageObject('ProfilePage') // await profilePage.goto(name) diff --git a/src/hooks/useProfileActions.ts b/src/hooks/useProfileActions.ts index 26195089c..287b886f7 100644 --- a/src/hooks/useProfileActions.ts +++ b/src/hooks/useProfileActions.ts @@ -291,7 +291,17 @@ export const useProfileActions = ({ name, enabled: enabled_ = true }: Props) => isAvailablePrimaryName, ownerData?.owner, ownerData?.registrant, - abilities, + abilities.canEdit, + abilities.canEditRecords, + abilities.canEditResolver, + abilities.canDelete, + abilities.canDeleteContract, + abilities.canDeleteError, + abilities.canReclaim, + abilities.canDeleteRequiresWrap, + abilities.isPCCBurned, + abilities.isParentOwner, + abilities.canDeleteMethod, t, hasGraphError, hasGraphErrorLoading, From e9f86a32e38299ff1d23431d24d2278059f9c222 Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Mon, 29 Jul 2024 21:45:28 +0700 Subject: [PATCH 12/13] Add E2E test case for edit profile button: "Should show disabled edit profile button if name is wrapped but use for edit resolver is burned and resolver is unauthorised" --- e2e/specs/stateless/profileEditor.spec.ts | 66 ++++++++++++++--------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/e2e/specs/stateless/profileEditor.spec.ts b/e2e/specs/stateless/profileEditor.spec.ts index 2df5402e6..832411dbc 100644 --- a/e2e/specs/stateless/profileEditor.spec.ts +++ b/e2e/specs/stateless/profileEditor.spec.ts @@ -930,7 +930,7 @@ test('Should show edit profile button if user is manager', async ({ const profilePage = makePageObject('ProfilePage') await profilePage.goto(name) - await login.connect() + await login.connect('user2') await expect(profilePage.editProfileButton).toBeVisible() }) @@ -956,28 +956,42 @@ test('Should show disabled edit profile button if user is owner but not manager' await expect(page.getByTestId('disabled-profile-action-Edit profile')).toBeVisible() }) -// test('Should show disabled edit profile button if name is wrapped but use for edit resolver is burned and resolver is unauthorised', async ({ -// page, -// login, -// makeName, -// makePageObject, -// }) => { -// const name = await makeName({ -// label: 'wrapped', -// type: 'wrapped', -// owner: 'user', -// resolver: ownedResolverAddress as Address, -// fuses: { -// named: ['CANNOT_UNWRAP', 'CANNOT_SET_RESOLVER'], -// }, -// }) - -// const profilePage = makePageObject('ProfilePage') - -// await profilePage.goto(name) -// await login.connect() - -// await page.pause() - -// await expect(page.getByTestId('disabled-profile-action-Edit profile')).toBeVisible() -// }) +test('Should show disabled edit profile button if name is wrapped but use for edit resolver is burned and resolver is unauthorised', async ({ + page, + login, + makeName, + makePageObject, +}) => { + const name = await makeName({ + label: 'wrapped', + type: 'wrapped', + owner: 'user', + }) + + const UNAUTHORISED_RESOLVER = '0xd7a4F6473f32aC2Af804B3686AE8F1932bC35750' + const profilePage = makePageObject('ProfilePage') + const morePage = makePageObject('MorePage') + const permissionsPage = makePageObject('PermissionsPage') + const transactionModal = makePageObject('TransactionModal') + + await profilePage.goto(name) + await login.connect() + + // Set resolver to unauthorized resolver + await morePage.goto(name) + await morePage.editResolverButton.click() + await page.getByTestId('custom-resolver-radio').check() + await page.getByTestId('dogfood').fill(UNAUTHORISED_RESOLVER) + await page.getByTestId('update-button').click() + await transactionModal.autoComplete() + + // Burn CSR fuse + await permissionsPage.goto(name) + await permissionsPage.burnChildPermissions(['CANNOT_UNWRAP', 'CANNOT_SET_RESOLVER'], name) + await transactionModal.autoComplete() + + // Validate that the edit profile button is disabled + await profilePage.goto(name) + + await expect(page.getByTestId('disabled-profile-action-Edit profile')).toBeVisible() +}) From 35d4fe40c9c7ce01a811773fec5a037e078c7e7c Mon Sep 17 00:00:00 2001 From: Nho Huynh Date: Wed, 31 Jul 2024 15:57:16 +0700 Subject: [PATCH 13/13] Change owner and manager user to makes it easier to read the makeName if we can assume user is the test user. --- e2e/specs/stateless/profileEditor.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/specs/stateless/profileEditor.spec.ts b/e2e/specs/stateless/profileEditor.spec.ts index 832411dbc..5ce4a5791 100644 --- a/e2e/specs/stateless/profileEditor.spec.ts +++ b/e2e/specs/stateless/profileEditor.spec.ts @@ -923,14 +923,14 @@ test('Should show edit profile button if user is manager', async ({ const name = await makeName({ label: 'unwrapped', type: 'legacy', - owner: 'user', - manager: 'user2', + owner: 'user2', + manager: 'user', }) const profilePage = makePageObject('ProfilePage') await profilePage.goto(name) - await login.connect('user2') + await login.connect() await expect(profilePage.editProfileButton).toBeVisible() })