Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identical expiry date pagination fix #908

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 232 additions & 3 deletions e2e/specs/stateless/myNames.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { expect } from '@playwright/test'
import { testClient } from '@root/playwright/fixtures/contracts/utils/addTestContracts'
import { createAccounts } from '@root/playwright/fixtures/accounts'
import {
testClient,
walletClient,
} from '@root/playwright/fixtures/contracts/utils/addTestContracts'
import { Address, labelhash } from 'viem'

import { deleteSubname } from '@ensdomains/ensjs/wallet'

import { test } from '../../../playwright'
import { Name } from '../../../playwright/fixtures/makeName'
Expand All @@ -18,7 +25,6 @@ test('myNames', async ({ page, login, makeName }) => {

await page.goto('/')
await login.connect('user2')
await page.pause()

await page.goto('/my/names')

Expand All @@ -32,6 +38,229 @@ test('myNames', async ({ page, login, makeName }) => {
)

expect(timestamps.every((timestamp) => timestamp === timestamps[0])).toBe(true)
})

test.describe.serial('myNames', () => {
test.beforeAll(async ({ subgraph }) => {
// Move time to the future to force previous names to expire
await testClient.increaseTime({ seconds: 2 * 365 * 24 * 60 * 60 })
await testClient.mine({ blocks: 1 })
await subgraph.sync()
})

let subnamesToDelete: string[] = []
let allNames: string[] = []

test.afterAll(async () => {
console.log('cleaning up subnames')
const account = createAccounts().getAddress('user4') as Address
for (const subname of subnamesToDelete) {
const contract = subname.includes('wrapped') ? 'nameWrapper' : 'registry'
console.log('deleting subname:', subname, 'on', contract)
// eslint-disable-next-line no-await-in-loop
await deleteSubname(walletClient, {
name: subname,
account,
contract,
})
}
})

const makeSubnamesConfig = (type: 'legacy' | 'wrapped') =>
Array.from(
{ length: 10 },
(_, i) =>
({
label: `sub${i}`,
owner: 'user4',
type,
...(type === 'wrapped'
? {
fuses: {
parent: {
named: ['PARENT_CANNOT_CONTROL'],
},
},
}
: {}),
}) as any,
)

test('should display all names for expiry date ASC', async ({ page, login, makeName }) => {
const earlierName = await makeName({
label: 'earlier-wrapped',
type: 'wrapped',
owner: 'user4',
fuses: {
named: ['CANNOT_UNWRAP'],
},
subnames: makeSubnamesConfig('wrapped'),
})
const concurrentNames = await makeName([
{
label: `concurrent-legacy`,
type: 'legacy',
owner: 'user4',
subnames: makeSubnamesConfig('legacy'),
} as Name,
{
label: `concurrent-wrapped`,
type: 'wrapped',
owner: 'user4',
fuses: {
named: ['CANNOT_UNWRAP'],
},
subnames: makeSubnamesConfig('wrapped'),
},
])
const laterName = await makeName({
label: 'later-legacy-name',
type: 'legacy',
owner: 'user4',
subnames: makeSubnamesConfig('legacy'),
})

subnamesToDelete = [earlierName, ...concurrentNames, laterName].flatMap((name) =>
Array.from({ length: 10 }, (_, i) => `sub${i}.${name}`),
)
allNames = [earlierName, ...concurrentNames, laterName, ...subnamesToDelete]

await page.goto('/')
await login.connect('user4')
await page.goto('/my/names')

await expect(page.getByTestId('names-list')).toBeVisible({ timeout: 10000 })

await page.evaluate(async () => {
let previousScrollHeight = 0
let { scrollHeight } = document.body
do {
window.scrollTo(0, scrollHeight)
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
previousScrollHeight = scrollHeight
scrollHeight = document.body.scrollHeight
} while (previousScrollHeight !== scrollHeight)
})

for (const name of allNames) {
const decryptedLocator = page.getByTestId(`name-item-${name}`)
const nameParts = name.split('.')
const label = nameParts.shift()!
const labelHash = `[${labelhash(label).replace('0x', '')}]`
const encryptedLocator = page.getByTestId(`name-item-${[labelHash, ...nameParts].join('.')}`)
// eslint-disable-next-line no-await-in-loop
await expect(decryptedLocator.or(encryptedLocator)).toBeVisible()
}
})

test('should display all names for expiry date DESC', async ({ page, login }) => {
await page.goto('/')
await login.connect('user4')
await page.goto('/my/names')

await expect(page.getByTestId('names-list')).toBeVisible({ timeout: 10000 })

await page.getByTestId('sort-desc').click()
await page.waitForTimeout(1000)

await page.evaluate(async () => {
let previousScrollHeight = 0
let { scrollHeight } = document.body
do {
window.scrollTo(0, scrollHeight)
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
previousScrollHeight = scrollHeight
scrollHeight = document.body.scrollHeight
} while (previousScrollHeight !== scrollHeight)
})

for (const name of allNames) {
const decryptedLocator = page.getByTestId(`name-item-${name}`)
const nameParts = name.split('.')
const label = nameParts.shift()!
const labelHash = `[${labelhash(label).replace('0x', '')}]`
const encryptedLocator = page.getByTestId(`name-item-${[labelHash, ...nameParts].join('.')}`)
// eslint-disable-next-line no-await-in-loop
await expect(decryptedLocator.or(encryptedLocator)).toBeVisible()
}
})

test('should display all names for createdAt ASC', async ({ page, login }) => {
await page.goto('/')
await login.connect('user4')
await page.goto('/my/names')

await expect(page.getByTestId('names-list')).toBeVisible({ timeout: 10000 })

await page.getByTestId('select-container').getByRole('button').click()
await page.getByTestId('select-option-createdAt').click()
await page.waitForTimeout(1000)

await page.evaluate(async () => {
let previousScrollHeight = 0
let { scrollHeight } = document.body
do {
window.scrollTo(0, scrollHeight)
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
previousScrollHeight = scrollHeight
scrollHeight = document.body.scrollHeight
} while (previousScrollHeight !== scrollHeight)
})

for (const name of allNames) {
const decryptedLocator = page.getByTestId(`name-item-${name}`)
const nameParts = name.split('.')
const label = nameParts.shift()!
const labelHash = `[${labelhash(label).replace('0x', '')}]`
const encryptedLocator = page.getByTestId(`name-item-${[labelHash, ...nameParts].join('.')}`)
// eslint-disable-next-line no-await-in-loop
await expect(decryptedLocator.or(encryptedLocator)).toBeVisible()
}
})

test('should display all names for createdAt DESC', async ({ page, login }) => {
await page.goto('/')
await login.connect('user4')
await page.goto('/my/names')

await expect(page.getByTestId('names-list')).toBeVisible({ timeout: 10000 })

await page.getByTestId('select-container').getByRole('button').click()
await page.getByTestId('select-option-createdAt').click()
await page.getByTestId('sort-desc').click()
await page.waitForTimeout(1000)

await page.evaluate(async () => {
let previousScrollHeight = 0
let { scrollHeight } = document.body
do {
window.scrollTo(0, scrollHeight)
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 1000)
})
previousScrollHeight = scrollHeight
scrollHeight = document.body.scrollHeight
} while (previousScrollHeight !== scrollHeight)
})

await page.pause()
for (const name of allNames) {
const decryptedLocator = page.getByTestId(`name-item-${name}`)
const nameParts = name.split('.')
const label = nameParts.shift()!
const labelHash = `[${labelhash(label).replace('0x', '')}]`
const encryptedLocator = page.getByTestId(`name-item-${[labelHash, ...nameParts].join('.')}`)
// eslint-disable-next-line no-await-in-loop
await expect(decryptedLocator.or(encryptedLocator)).toBeVisible()
}
})
})
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@
"knip:fix": "knip --fix --allow-remove-files"
},
"dependencies": {
"@adraffy/ens-normalize": "1.10.1",
"@ensdomains/address-encoder": "1.1.1",
"@ensdomains/content-hash": "^3.0.0-beta.5",
"@ensdomains/ens-contracts": "1.2.0-beta.0",
"@ensdomains/ensjs": "4.0.0",
"@ensdomains/ensjs": "4.0.2-alpha.5",
"@ensdomains/thorin": "0.6.50",
"@metamask/post-message-stream": "^6.1.2",
"@metamask/providers": "^14.0.2",
Expand Down Expand Up @@ -207,4 +208,4 @@
}
},
"packageManager": "[email protected]"
}
}
5 changes: 2 additions & 3 deletions playwright/fixtures/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ const shortenAddress = (address = '', maxLength = 10, leftSlice = 5, rightSlice

export type Accounts = ReturnType<typeof createAccounts>

export type User = 'user' | 'user2' | 'user3'
const users = ['user', 'user2', 'user3', 'user4'] as const
export type User = typeof users[number]

export const createAccounts = (stateful = false) => {
const mnemonic = stateful ? process.env.SECRET_WORDS || DEFAULT_MNEMONIC : DEFAULT_MNEMONIC

const users: User[] = ['user', 'user2', 'user3']

const { accounts, privateKeys } = users.reduce<{ accounts: Account[]; privateKeys: Hash[] }>(
(acc, _, index) => {
const { getHdKey } = mnemonicToAccount(mnemonic, { addressIndex: index })
Expand Down
17 changes: 8 additions & 9 deletions playwright/fixtures/makeName/generators/legacyNameGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,15 @@ export const makeLegacyNameGenerator = ({ accounts }: Dependencies) => ({
configure: async (nameConfig: LegacyName) => {
const { label, owner, manager, subnames = [], secret } = nameWithDefaults(nameConfig)
const name = `${label}.eth`

// Create subnames
await Promise.all(
subnames.map((subname) => {
return generateLegacySubname({ accounts })({
...subname,
name: `${label}.eth`,
nameOwner: owner,
})
}),
)
for (const subname of subnames) {
await generateLegacySubname({ accounts })({
...subname,
name: `${label}.eth`,
nameOwner: owner,
})
}

if (!!manager && manager !== owner) {
console.log('setting manager:', name, manager)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,14 @@ export const makeLegacyWithConfigNameGenerator = ({ accounts }: Dependencies) =>
await generateRecords({ accounts })({ name: `${label}.eth`, owner, resolver, records })

// Create subnames
await Promise.all(
subnames.map((subname) =>
generateLegacySubname({ accounts })({
...subname,
name,
nameOwner: owner,
resolver: subname.resolver ?? _resolver,
}),
),
)
for (const subname of subnames) {
await generateLegacySubname({ accounts })({
...subname,
name,
nameOwner: owner,
resolver: subname.resolver ?? _resolver,
})
}

// Set resolver if not valid
if (!hasValidResolver && resolver) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,16 +159,14 @@ export const makeWrappedNameGenerator = ({ accounts }: Dependencies) => ({
})
}

await Promise.all(
subnames.map((subname) =>
generateWrappedSubname({ accounts })({
for (const subname of subnames) {
await generateWrappedSubname({ accounts })({
...subname,
name: `${label}.eth`,
nameOwner: owner,
resolver: subname.resolver ?? _resolver,
}),
),
)
})
}

if (!hasValidResolver && resolver) {
console.log('setting resolver: ', name, resolver)
Expand Down
19 changes: 11 additions & 8 deletions playwright/fixtures/makeName/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,21 @@ export function createMakeNames({ accounts, time, subgraph }: Dependencies) {

await testClient.setAutomine(true)

// Finish setting up names
await Promise.all(
adjustedNames.map((name) => {
// Make sure that registration and subnames are on different block or it might cause the subgraph to crash due to
// RegisterName and TransferName event having the same event ids.
await testClient.mine({ blocks: 1 })

// Finish setting up names
for (const name of adjustedNames) {
if (isWrappendName(name)) {
return wrappedNameGenerator.configure(name)
await wrappedNameGenerator.configure(name)
} else if (isLegacyName(name)) {
return legacyRegisterNameGenerator.configure(name)
console.log('registering legacy name:', name)
await legacyRegisterNameGenerator.configure(name)
} else {
return legacyNameGenerator.configure(name)
await legacyNameGenerator.configure(name)
}
}),
)
}

if (offset > 0) {
console.warn('You are increasing the block timestamp. Do not run this test in parallel mode.')
Expand Down
Loading
Loading