Skip to content

Commit

Permalink
Merge pull request #162 from cytechmobile/feat/onboarding-e2e-tests
Browse files Browse the repository at this point in the history
test(e2e): onboarding tests
  • Loading branch information
QZera authored Jan 7, 2025
2 parents b21bb12 + cf74866 commit c5ae6a5
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 59 deletions.
13 changes: 13 additions & 0 deletions test/e2e/constants/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,16 @@ export const e2eTestDirPath = join(__dirname, '..')
* files outside of the e2e test directory.
*/
export const rootDirPath = join(__dirname, '../../..')

/**
* The path to the node home directory. This is where the Radicle node stores its
* configuration and data.
*/
export const nodeHomePath = process.env['RAD_HOME']

/**
* The path to a backup node home directory. This is used to store the original
* node home directory for tests that require the original path to be moved or
* modified.
*/
export const backupNodeHomePath = `${nodeHomePath}.backup`
15 changes: 15 additions & 0 deletions test/e2e/helpers/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Workbench } from 'wdio-vscode-service'

/**
* Opens the Radicle view container in the sidebar, by clicking the radicle button in the
* activity bar.
*/
export async function openRadicleViewContainer(workbench: Workbench) {
const activityBar = workbench.getActivityBar()
await activityBar.wait()

const radicleViewControl = await activityBar.getViewControl('Radicle')
await radicleViewControl?.wait()

await radicleViewControl?.openView()
}
28 changes: 28 additions & 0 deletions test/e2e/helpers/assertions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { browser } from '@wdio/globals'
import type { Workbench } from 'wdio-vscode-service'

/**
* Asserts that the CLI Commands and Patches sections are visible in the sidebar. This is
* considered the default state when the workspace is open with a git and rad initialized
* repository.
*/
export async function expectStandardSidebarViewsToBeVisible(workbench: Workbench) {
const sidebarView = workbench.getSideBar().getContent()
await sidebarView.wait()

await browser.waitUntil(
async () => {
try {
await Promise.all([
sidebarView.getSection('CLI COMMANDS'),
sidebarView.getSection('PATCHES'),
])

return true
} catch {
return false
}
},
{ timeoutMsg: 'expected the standard sidebar views to be visible' },
)
}
21 changes: 21 additions & 0 deletions test/e2e/helpers/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Workbench } from 'wdio-vscode-service'

/**
* Retrieves the welcome text content from the first section in the sidebar view.
*
* @example await getFirstWelcomeViewText(workbench) // ["Welcome to Radicle!", "Get started by ..."]
*
* @returns The text content found as an array of strings split by newlines. If no content is
* found, an empty array.
*/
export async function getFirstWelcomeViewText(workbench: Workbench) {
const sidebarView = workbench.getSideBar().getContent()
await sidebarView.wait()

const welcomeText =
(await (
await (await sidebarView.getSections())[0]?.findWelcomeContent()
)?.getTextSections()) ?? []

return welcomeText
}
140 changes: 82 additions & 58 deletions test/e2e/specs/onboarding.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import path from 'node:path'
import { browser, expect } from '@wdio/globals'
import type { ViewSection, Workbench } from 'wdio-vscode-service'
import type { Workbench } from 'wdio-vscode-service'
import { $, cd } from 'zx'
import type * as VsCode from 'vscode'
import { e2eTestDirPath } from '../constants/config'
import isEqual from 'lodash/isEqual'
import { expectStandardSidebarViewsToBeVisible } from '../helpers/assertions'
import { openRadicleViewContainer } from '../helpers/actions'
import { getFirstWelcomeViewText } from '../helpers/queries'
import { backupNodeHomePath, e2eTestDirPath, nodeHomePath } from '../constants/config'

describe('Onboarding Flow', () => {
let workbench: Workbench

before(async () => {
await initGitRepo()
workbench = await browser.getWorkbench()
})

describe('VS Code, *before* the workspace is rad-initialized,', () => {
describe('VS Code, *before* Radicle is installed,', () => {
after(async () => {
await $`mv ${backupNodeHomePath} ${nodeHomePath}`
await workbench.executeCommand('Developer: Reload Window')
})

it('has our Radicle extension installed and available', async () => {
const extensions = await browser.executeWorkbench(
(vscode: typeof VsCode) => vscode.extensions.all,
Expand All @@ -31,68 +39,92 @@ describe('Onboarding Flow', () => {
expect(title).toBe('Radicle')
})

it('guides the user on how to rad-initialize their git repo', async () => {
it('instructs the user to install radicle', async () => {
await openRadicleViewContainer(workbench)

const welcomeText = await getFirstWelcomeViewText(workbench)
const buttonTitles = await getFirstWelcomeViewButtonTitles(workbench)

expect(welcomeText).toEqual([
/* eslint-disable max-len */
'The Git repository currently opened in your workspace is not yet initialized with Radicle.',
'To use Radicle with it, please run `rad init` in your terminal.',
'Once rad-initialized, this repo will have access to advanced source control, collaboration and project management capabilities powered by both Git and Radicle.',
'During this reversible rad-initializing process you also get to choose whether your repo will be private or public, among other options.',
'To learn more read the Radicle User Guide.',
'Failed resolving the Radicle CLI binary.',
"Please ensure it is installed on your machine and either that it is globally accessible in the shell as `rad` or that its path is correctly defined in the extension's settings.",
"Please expect the extention's capabilities to remain severely limited until this issue is resolved.",
/* eslint-enable max-len */
])

expect(welcomeText.some((text) => text.includes('rad init'))).toBe(true)
expect(buttonTitles).toEqual(['Troubleshoot'])
})
})

describe('VS Code, *after* the workspace is rad-initialized,', () => {
let cliCommandsSection: ViewSection
describe('VS Code, *before* the workspace is git-initialized,', () => {
it('guides the user on how to git-initialize their workspace', async () => {
await openRadicleViewContainer(workbench)

await browser.waitUntil(async () => {
const welcomeText = await getFirstWelcomeViewText(workbench)
const welcomeButtonTitles = await getFirstWelcomeViewButtonTitles(workbench)

return (
isEqual(welcomeText, [
/* eslint-disable max-len */
'The folder currently opened in your workspace is not a Git code repository.',
'In order to use Radicle with it, this folder must first be initialized as a Git code repository.',
'To learn more about how to use Git and source control in VS Code read the docs.',
/* eslint-enable max-len */
]) &&
isEqual(welcomeButtonTitles, [
'Initialize Repository With Git',
'Choose a Different Folder',
])
)
})
})
})

describe('VS Code, *before* the workspace is rad-initialized,', () => {
before(async () => {
await $`rad init --private --default-branch main --name "A_test_blog" --description "Some repo" --no-confirm --verbose`
await initGitRepo()
})

it('guides the user on how to rad-initialize their git repo', async () => {
await openRadicleViewContainer(workbench)
const sidebarView = workbench.getSideBar().getContent()
await sidebarView.wait()

cliCommandsSection = await sidebarView.getSection('CLI COMMANDS')
await cliCommandsSection.collapse()
await browser.waitUntil(async () => {
const welcomeText = await getFirstWelcomeViewText(workbench)

return isEqual(welcomeText, [
/* eslint-disable max-len */
'The Git repository currently opened in your workspace is not yet initialized with Radicle.',
'To use Radicle with it, please run `rad init` in your terminal.',
'Once rad-initialized, this repo will have access to advanced source control, collaboration and project management capabilities powered by both Git and Radicle.',
'During this reversible rad-initializing process you also get to choose whether your repo will be private or public, among other options.',
'To learn more read the Radicle User Guide.',
/* eslint-enable max-len */
])
})
})
})

const patchesSection = await sidebarView.getSection('PATCHES')
await patchesSection.collapse()
describe('VS Code, *after* the workspace is rad-initialized,', () => {
before(async () => {
await $`rad init --private --default-branch main --name "A_test_blog" --description "Some repo" --no-confirm --verbose`
await workbench.executeCommand('Developer: Reload Window')
})

it('hides the non rad-initialized guide', async () => {
const welcomeText = await getFirstWelcomeViewText(workbench)
await browser.waitUntil(async () => {
const welcomeText = await getFirstWelcomeViewText(workbench)

expect(welcomeText.some((text) => text.includes('rad init'))).not.toBe(true)
return welcomeText.some((text) => text.includes('rad init')) === false
})
})

it('shows the CLI Commands section', async () => {
await cliCommandsSection.expand()
// Fixes flakiness on macOS CI
await browser.pause(100)

const welcomeContent = await cliCommandsSection?.findWelcomeContent()
const welcomeText = (await welcomeContent?.getTextSections()) ?? []
const buttons = (await welcomeContent?.getButtons()) ?? []
const buttonTitles = await Promise.all(
buttons.map(async (button) => await button.getTitle()),
)

expect(
welcomeText.some((text) =>
/Use the buttons below to perform common interactions with the Radicle network./i.test(
text,
),
),
).toBe(true)
it('shows the standard sidebar views', async () => {
const sidebarView = workbench.getSideBar().getContent()
await sidebarView.wait()

expect(buttonTitles).toEqual(['Sync', 'Fetch', 'Announce'])
await expectStandardSidebarViewsToBeVisible(workbench)
})
})
})
Expand All @@ -111,24 +143,16 @@ async function initGitRepo() {
await $`git commit -m 'adds readme' --no-gpg-sign`
}

async function openRadicleViewContainer(workbench: Workbench) {
const activityBar = workbench.getActivityBar()
await activityBar.wait()

const radicleViewControl = await activityBar.getViewControl('Radicle')
await radicleViewControl?.wait()

await radicleViewControl?.openView()
}

async function getFirstWelcomeViewText(workbench: Workbench) {
async function getFirstWelcomeViewButtonTitles(workbench: Workbench) {
const sidebarView = workbench.getSideBar().getContent()
await sidebarView.wait()

const welcomeText =
(await (
await (await sidebarView.getSections())[0]?.findWelcomeContent()
)?.getTextSections()) ?? []
const buttons =
(await (await (await sidebarView.getSections())[0]?.findWelcomeContent())?.getButtons()) ??
[]
const buttonTitles = await Promise.all(
buttons.map(async (button) => await button.getTitle()),
)

return welcomeText
return buttonTitles
}
12 changes: 11 additions & 1 deletion test/e2e/wdio.conf.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import path from 'node:path'
import { $ } from 'zx'
import type { Options } from '@wdio/types'
import { e2eTestDirPath, rootDirPath } from './constants/config'
import {
backupNodeHomePath,
e2eTestDirPath,
nodeHomePath,
rootDirPath,
} from './constants/config'

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const packageJson = require('../../package.json')
Expand Down Expand Up @@ -56,4 +61,9 @@ export const config: Options.Testrunner = {
onPrepare: async () => {
await $`mkdir -p ${path.join(rootDirPath, 'node_modules/.cache/wdio')}`
},
onWorkerStart: async (_cid, _caps, specs) => {
if (specs.some((spec) => spec.includes('onboarding.spec.ts'))) {
await $`mv ${nodeHomePath} ${backupNodeHomePath}`
}
},
}

0 comments on commit c5ae6a5

Please sign in to comment.