Skip to content

Commit

Permalink
chore: fix sidebars
Browse files Browse the repository at this point in the history
  • Loading branch information
molant committed Nov 4, 2021
1 parent fca1630 commit b1b40ab
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 286 deletions.
3 changes: 2 additions & 1 deletion docs/latest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ an issue:
* [Testing and Debugging](latest/tutorial/application-debugging.md)
* [Debugging the Main Process](latest/tutorial/debugging-main-process.md)
* [Debugging with Visual Studio Code](latest/tutorial/debugging-vscode.md)
* [Using Selenium and WebDriver](latest/tutorial/using-selenium-and-webdriver.md)
* [Testing on Headless CI Systems (Travis, Jenkins)](latest/tutorial/testing-on-headless-ci.md)
* [DevTools Extension](latest/tutorial/devtools-extension.md)
* [Automated Testing](latest/tutorial/automated-testing.md)
* [Automated Testing with a Custom Driver](latest/tutorial/automated-testing-with-a-custom-driver.md)
* [REPL](latest/tutorial/repl.md)
* [Distribution](latest/tutorial/application-distribution.md)
* [Supported Platforms](latest/tutorial/support.md#supported-platforms)
Expand Down
8 changes: 4 additions & 4 deletions docs/latest/api/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Returns:
* `launchInfo` Record<string, any&#62; | [NotificationResponse](latest/api/structures/notification-response.md) _macOS_

Emitted once, when Electron has finished initializing. On macOS, `launchInfo`
holds the `userInfo` of the [`NSUserNotification`](https://developer.apple.com/documentation/foundation/nsusernotification)
or information from [`UNNotificationResponse`](https://developer.apple.com/documentation/usernotifications/unnotificationresponse)
that was used to open the application, if it was launched from Notification Center.
You can also call `app.isReady()` to check if this event has already fired and `app.whenReady()`
holds the `userInfo` of the `NSUserNotification` or information from
[`UNNotificationResponse`](latest/api/structures/notification-response.md) that was used to open the
application, if it was launched from Notification Center. You can also call
`app.isReady()` to check if this event has already fired and `app.whenReady()`
to get a Promise that is fulfilled when Electron is initialized.

### Event: 'window-all-closed'
Expand Down
2 changes: 1 addition & 1 deletion docs/latest/api/browser-window.md
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,7 @@ current window into a top-level window.

#### `win.getParentWindow()`

Returns `BrowserWindow | null` - The parent window or `null` if there is no parent.
Returns `BrowserWindow` - The parent window.

#### `win.getChildWindows()`

Expand Down
2 changes: 1 addition & 1 deletion docs/latest/api/clipboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ Returns `Boolean` - Whether the clipboard supports the specified `format`.
```js
const { clipboard } = require('electron')

const hasFormat = clipboard.has('public/utf8-plain-text')
const hasFormat = clipboard.has('<p>selection</p>')
console.log(hasFormat)
// 'true' or 'false'
```
Expand Down
49 changes: 47 additions & 2 deletions docs/latest/tutorial/accessibility.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,55 @@
---
title: "Accessibility"
description: "Accessibility concerns in Electron applications are similar to those of websites because they're both ultimately HTML."
description: "Making accessible applications is important and we're happy to provide functionality to Devtron and Spectron that gives developers the opportunity to make their apps better for everyone."
slug: accessibility
hide_title: false
---

# Accessibility

Making accessible applications is important and we're happy to provide
functionality to [Devtron][devtron] and [Spectron][spectron] that gives
developers the opportunity to make their apps better for everyone.

---

Accessibility concerns in Electron applications are similar to those of
websites because they're both ultimately HTML.
websites because they're both ultimately HTML. With Electron apps, however,
you can't use the online resources for accessibility audits because your app
doesn't have a URL to point the auditor to.

These features bring those auditing tools to your Electron app. You can
choose to add audits to your tests with Spectron or use them within DevTools
with Devtron. Read on for a summary of the tools.

## Spectron

In the testing framework Spectron, you can now audit each window and `<webview>`
tag in your application. For example:

```javascript
app.client.auditAccessibility().then(function (audit) {
if (audit.failed) {
console.error(audit.message)
}
})
```

You can read more about this feature in [Spectron's documentation][spectron-a11y].

## Devtron

In Devtron, there is an accessibility tab which will allow you to audit a
page in your app, sort and filter the results.

![devtron screenshot][devtron-screenshot]

Both of these tools are using the [Accessibility Developer Tools][a11y-devtools]
library built by Google for Chrome. You can learn more about the accessibility
audit rules this library uses on that [repository's wiki][a11y-devtools-wiki].

If you know of other great accessibility tools for Electron, add them to the
accessibility documentation with a pull request.

## Manually enabling accessibility features

Expand Down Expand Up @@ -50,6 +91,10 @@ CFStringRef kAXManualAccessibility = CFSTR("AXManualAccessibility");
}
```

[devtron]: https://electronjs.org/devtron
[devtron-screenshot]: https://cloud.githubusercontent.com/assets/1305617/17156618/9f9bcd72-533f-11e6-880d-389115f40a2a.png
[spectron]: https://electronjs.org/spectron
[spectron-a11y]: https://github.com/electron/spectron#accessibility-testing
[a11y-docs]: https://www.chromium.org/developers/design-documents/accessibility#TOC-How-Chrome-detects-the-presence-of-Assistive-Technology
[a11y-devtools]: https://github.com/GoogleChrome/accessibility-developer-tools
[a11y-devtools-wiki]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules
Expand Down
142 changes: 142 additions & 0 deletions docs/latest/tutorial/automated-testing-with-a-custom-driver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
title: "Automated Testing with a Custom Driver"
description: "To write automated tests for your Electron app, you will need a way to \"drive\" your application. Spectron is a commonly-used solution which lets you emulate user actions via WebDriver. However, it's also possible to write your own custom driver using node's builtin IPC-over-STDIO. The benefit of a custom driver is that it tends to require less overhead than Spectron, and lets you expose custom methods to your test suite."
slug: automated-testing-with-a-custom-driver
hide_title: false
---

# Automated Testing with a Custom Driver

To write automated tests for your Electron app, you will need a way to "drive" your application. [Spectron](https://electronjs.org/spectron) is a commonly-used solution which lets you emulate user actions via [WebDriver](https://webdriver.io/). However, it's also possible to write your own custom driver using node's builtin IPC-over-STDIO. The benefit of a custom driver is that it tends to require less overhead than Spectron, and lets you expose custom methods to your test suite.

To create a custom driver, we'll use Node.js' [child_process](https://nodejs.org/api/child_process.html) API. The test suite will spawn the Electron process, then establish a simple messaging protocol:

```js
const childProcess = require('child_process')
const electronPath = require('electron')

// spawn the process
const env = { /* ... */ }
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })

// listen for IPC messages from the app
appProcess.on('message', (msg) => {
// ...
})

// send an IPC message to the app
appProcess.send({ my: 'message' })
```

From within the Electron app, you can listen for messages and send replies using the Node.js [process](https://nodejs.org/api/process.html) API:

```js
// listen for IPC messages from the test suite
process.on('message', (msg) => {
// ...
})

// send an IPC message to the test suite
process.send({ my: 'message' })
```

We can now communicate from the test suite to the Electron app using the `appProcess` object.

For convenience, you may want to wrap `appProcess` in a driver object that provides more high-level functions. Here is an example of how you can do this:

```js
class TestDriver {
constructor ({ path, args, env }) {
this.rpcCalls = []

// start child process
env.APP_TEST_DRIVER = 1 // let the app know it should listen for messages
this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })

// handle rpc responses
this.process.on('message', (message) => {
// pop the handler
const rpcCall = this.rpcCalls[message.msgId]
if (!rpcCall) return
this.rpcCalls[message.msgId] = null
// reject/resolve
if (message.reject) rpcCall.reject(message.reject)
else rpcCall.resolve(message.resolve)
})

// wait for ready
this.isReady = this.rpc('isReady').catch((err) => {
console.error('Application failed to start', err)
this.stop()
process.exit(1)
})
}

// simple RPC call
// to use: driver.rpc('method', 1, 2, 3).then(...)
async rpc (cmd, ...args) {
// send rpc request
const msgId = this.rpcCalls.length
this.process.send({ msgId, cmd, args })
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
}

stop () {
this.process.kill()
}
}
```

In the app, you'd need to write a simple handler for the RPC calls:

```js
if (process.env.APP_TEST_DRIVER) {
process.on('message', onMessage)
}

async function onMessage ({ msgId, cmd, args }) {
let method = METHODS[cmd]
if (!method) method = () => new Error('Invalid method: ' + cmd)
try {
const resolve = await method(...args)
process.send({ msgId, resolve })
} catch (err) {
const reject = {
message: err.message,
stack: err.stack,
name: err.name
}
process.send({ msgId, reject })
}
}

const METHODS = {
isReady () {
// do any setup needed
return true
}
// define your RPC-able methods here
}
```

Then, in your test suite, you can use your test-driver as follows:

```js
const test = require('ava')
const electronPath = require('electron')

const app = new TestDriver({
path: electronPath,
args: ['./app'],
env: {
NODE_ENV: 'test'
}
})
test.before(async t => {
await app.isReady
})
test.after.always('cleanup', async t => {
await app.stop()
})
```
Loading

0 comments on commit b1b40ab

Please sign in to comment.