Skip to content

Commit

Permalink
Merge pull request #876 from ensdomains/simulate-registration-overrid…
Browse files Browse the repository at this point in the history
…e-new

add simulate registration override
  • Loading branch information
LeonmanRolls authored Sep 27, 2024
2 parents e2144c3 + fbffc6e commit 6c4619d
Show file tree
Hide file tree
Showing 16 changed files with 609 additions and 95 deletions.
8 changes: 7 additions & 1 deletion e2e/specs/stateless/extendNames.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ test('should be able to register multiple names on the address page', async ({
subgraph,
makePageObject,
makeName,
time,
}) => {
// Generating names in not neccessary but we want to make sure that there are names to extend
await makeName([
Expand Down Expand Up @@ -83,11 +84,16 @@ test('should be able to register multiple names on the address page', async ({
await transactionModal.autoComplete()

await subgraph.sync()
await page.reload()
await page.waitForTimeout(3000)

// Should be able to remove this after useQuery is fixed. Using to force a refetch.
await time.increaseTime({ seconds: 15 })
await page.reload()
for (const name of extendableNameItems) {
const label = name.replace('.eth', '')
await addresPage.search(label)
await expect(addresPage.getNameRow(name)).toBeVisible({ timeout: 5000 })
await page.pause()
await expect(await addresPage.getTimestamp(name)).not.toBe(timestampDict[name])
await expect(await addresPage.getTimestamp(name)).toBe(timestampDict[name] + 31536000000 * 3)
}
Expand Down
136 changes: 132 additions & 4 deletions e2e/specs/stateless/registerName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ test.describe.serial('normal registration', () => {
accounts,
time,
makePageObject,
consoleListener,
}) => {
await setPrimaryName(walletClient, {
name: '',
Expand All @@ -39,8 +40,10 @@ test.describe.serial('normal registration', () => {
const registrationPage = makePageObject('RegistrationPage')
const transactionModal = makePageObject('TransactionModal')

await time.sync(500)

await consoleListener.initialize({
regex: /Event triggered on local development.*register-override-triggered/,
})
await time.sync()
await homePage.goto()
await login.connect()

Expand Down Expand Up @@ -100,6 +103,7 @@ test.describe.serial('normal registration', () => {
await page.getByTestId('next-button').click()
await transactionModal.closeButton.click()

await page.pause()
await expect(
page.getByText(
'You will need to complete two transactions to secure your name. The second transaction must be completed within 24 hours of the first.',
Expand All @@ -117,15 +121,19 @@ test.describe.serial('normal registration', () => {
),
).toBeVisible()

await time.sync()

// should show countdown
await expect(page.getByTestId('countdown-circle')).toBeVisible()
await expect(page.getByTestId('countdown-complete-check')).toBeVisible()
await expect(page.getByTestId('countdown-complete-check')).not.toBeVisible()
const waitButton = page.getByTestId('wait-button')
await expect(waitButton).toBeVisible()
await expect(waitButton).toBeDisabled()

await time.increaseTime({ seconds: 60 })
await expect(page.getByTestId('countdown-complete-check')).toBeVisible()
const startTimerButton = page.getByTestId('start-timer-button')
await expect(startTimerButton).not.toBeVisible()
await testClient.increaseTime({ seconds: 60 })

// Should show registration text
await expect(
Expand Down Expand Up @@ -156,6 +164,10 @@ test.describe.serial('normal registration', () => {
await expect(page.getByTestId('address-profile-button-eth')).toHaveText(
accounts.getAddress('user', 5),
)

await test.step('confirm that track event was not called', async () => {
await expect(consoleListener.getMessages()).toHaveLength(0)
})
})

test('should not direct to the registration page on search, and show all records from registration', async ({
Expand Down Expand Up @@ -931,3 +943,119 @@ test('should be able to detect an existing commit created on a private mempool',
)
})
})

test.describe('Error handling', () => {
test('should be able to detect an existing commit created on a private mempool', async ({
page,
login,
time,
makePageObject,
}) => {
test.slow()

const homePage = makePageObject('HomePage')
const registrationPage = makePageObject('RegistrationPage')
const transactionModal = makePageObject('TransactionModal')

await time.sync()

await homePage.goto()
await login.connect()

const name = `expired-commit-${Date.now()}.eth`
// should redirect to registration page
await homePage.searchInput.fill(name)
await homePage.searchInput.press('Enter')
await expect(page.getByRole('heading', { name: `Register ${name}` })).toBeVisible()

await test.step('pricing page', async () => {
await page.getByTestId('payment-choice-ethereum').check()
await registrationPage.primaryNameToggle.uncheck()
await page.getByTestId('next-button').click()
})

await test.step('info page', async () => {
await expect(page.getByTestId('next-button')).toHaveText('Begin')
await page.getByTestId('next-button').click()
})

await test.step('transaction: commit', async () => {
await expect(page.getByText('Open Wallet')).toBeVisible()
await transactionModal.confirm()
await expect(page.getByText(`Your "Start timer" transaction was successful`)).toBeVisible()
await time.sync()
await page.waitForTimeout(1000)
await time.increaseTime({ seconds: 60 * 60 * 24 })
})

await expect(
page.getByText('Your registration has expired. You will need to start the process again.'),
).toBeVisible()
await expect(page.getByRole('button', { name: 'Restart' })).toBeVisible()
await expect(page.getByTestId('finish-button')).toHaveCount(0)
})

test('should be able to register name if the commit transaction does not update', async ({
page,
login,
accounts,
time,
makePageObject,
consoleListener,
}) => {
test.slow()

const homePage = makePageObject('HomePage')
const registrationPage = makePageObject('RegistrationPage')
const transactionModal = makePageObject('TransactionModal')

await time.sync()
await consoleListener.initialize({
regex: /Event triggered on local development.*register-override-triggered/,
})
await homePage.goto()
await login.connect()

const name = `stuck-commit-${Date.now()}.eth`
// should redirect to registration page
await homePage.searchInput.fill(name)
await homePage.searchInput.press('Enter')
await expect(page.getByRole('heading', { name: `Register ${name}` })).toBeVisible()

await test.step('pricing page', async () => {
await page.getByTestId('payment-choice-ethereum').check()
await registrationPage.primaryNameToggle.uncheck()
await page.getByTestId('next-button').click()
})

await test.step('info page', async () => {
await expect(page.getByTestId('next-button')).toHaveText('Begin')
await page.getByTestId('next-button').click()
})

await test.step('transaction: commit', async () => {
await expect(page.getByText('Open Wallet')).toBeVisible()
await transactionModal.confirm()
await expect(page.getByText(`Your "Start timer" transaction was successful`)).toBeVisible()
await time.increaseTimeByTimestamp({ seconds: 120 })
})

await test.step('transaction: register', async () => {
await expect(page.getByTestId('finish-button')).toBeVisible({ timeout: 10000 })
await expect(page.getByTestId('finish-button')).toBeEnabled()

await page.getByTestId('finish-button').click()
await expect(page.getByText('Open Wallet')).toBeVisible()
await transactionModal.confirm()

await page.getByTestId('view-name').click()
await expect(page.getByTestId('address-profile-button-eth')).toHaveText(
accounts.getAddress('user', 5),
)
})

await test.step('confirm plausible event was fired once', async () => {
expect(consoleListener.getMessages()).toHaveLength(1)
})
})
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
"@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@^0.3.0-beta.13",
"@openzeppelin/contracts": "^4.7.3",
"@openzeppelin/test-helpers": "^0.5.16",
"@playwright/test": "^1.36.2",
"@playwright/test": "^1.45.0",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^14.0.0",
"@testing-library/react-hooks": "^8.0.1",
Expand Down
30 changes: 30 additions & 0 deletions playwright/fixtures/consoleListener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ConsoleMessage, Page } from "@playwright/test"

type Dependencies = {
page: Page
}

export const createConsoleListener = ({ page}: Dependencies) => {
let messages: string[] = []
let internalRegex: RegExp | null = null

const filter = (msg: ConsoleMessage) => {
const message = msg.text()
if (internalRegex?.test(message)) messages.push(message)
}

return {
initialize: ({ regex}: { regex: RegExp}) => {
messages.length = 0
internalRegex = regex
page.on('console', filter)
},
reset: () => {
messages.length = 0
internalRegex = null
page.off('console', filter)
},
print: () => console.log(messages),
getMessages: () => messages
}
}
69 changes: 43 additions & 26 deletions playwright/fixtures/time.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import { Page } from '@playwright/test'

import { publicClient } from './contracts/utils/addTestContracts'
import { publicClient, testClient } from './contracts/utils/addTestContracts'

export type Time = ReturnType<typeof createTime>

Expand All @@ -11,33 +11,50 @@ type Dependencies = {

export const createTime = ({ page }: Dependencies) => {
return {
// Offset is used to set the browser forward in time. This is useful for testing contract where
// the contract relies on block timestamp, but anvil's block timestamp is unpredictable.
sync: async (offset = 0) => {
const browserTime = await page.evaluate(() => Math.floor(Date.now() / 1000))
const blockTime = Number((await publicClient.getBlock()).timestamp)
const browserOffset = (blockTime - browserTime + offset) * 1000

console.log(`Browser time: ${new Date(Date.now() + browserOffset)}`)

await page.addInitScript(`{
// Prevents Date from being extended multiple times
if (Object.getPrototypeOf(Date).name !== 'Date') {
const __DateNow = Date.now
const browserOffset = ${browserOffset};
Date = class extends Date {
constructor(...args) {
if (args.length === 0) {
super(__DateNow() + browserOffset);
} else {
super(...args);
}
}
static now() {
return super.now() + browserOffset;
}
}
}
}`)
const time = new Date((blockTime + offset) * 1000)
console.log(`Browser time: ${time}`)
await page.clock.install({ time })
},
logBlockTime: async () => {
const blockTime = Number((await publicClient.getBlock()).timestamp)
console.log(`Block time: ${new Date(blockTime * 1000)}`)
},
logBrowserTime: async () => {
const time = await page.evaluate(() => new Date().toString())
console.log(`Browser time: ${time}`)
},
syncFixed: async () => {
const blockTime = Number((await publicClient.getBlock()).timestamp)
const time = new Date(blockTime * 1000)
await page.clock.setFixedTime(time)
console.log(`Fixed Browser time: ${time}`, blockTime)
},
increaseTime: async ({ seconds }: { seconds: number }) => {
await testClient.increaseTime({ seconds })
await page.clock.fastForward(seconds * 1000)
},
increaseTimeByTimestamp: async ({ seconds }: { seconds: number }) => {
const tryIncreaseTime = async () => {
try {
const blockTimestamp = Number((await publicClient.getBlock()).timestamp)
await testClient.setNextBlockTimestamp({ timestamp: BigInt(blockTimestamp + seconds) })
await testClient.mine({ blocks: 1 })
return true
} catch {
return false
}
}

let success = false
let attempts = 0
while (!success && attempts < 3) {
success = await tryIncreaseTime()
attempts += 1
}
}
}
}
7 changes: 7 additions & 0 deletions playwright/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { createMakeNames } from './fixtures/makeName/index.js'
import { createSubgraph } from './fixtures/subgraph.js'
import { createTime } from './fixtures/time.js'
import { createPageObjectMaker } from './pageObjects/index.js'
import { createConsoleListener } from './fixtures/consoleListener'

type Fixtures = {
accounts: Accounts
Expand All @@ -20,6 +21,7 @@ type Fixtures = {
makePageObject: ReturnType<typeof createPageObjectMaker>
subgraph: ReturnType<typeof createSubgraph>
time: ReturnType<typeof createTime>
consoleListener: ReturnType<typeof createConsoleListener>
}

export const test = base.extend<Fixtures>({
Expand Down Expand Up @@ -57,4 +59,9 @@ export const test = base.extend<Fixtures>({
time: async ({ page }, use) => {
await use(createTime({ page }))
},
consoleListener: async ({ page }, use) => {
const consoleListener = createConsoleListener({ page })
await use(consoleListener)
consoleListener.reset()
}
})
Loading

0 comments on commit 6c4619d

Please sign in to comment.