extensions-spec.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { expect } from 'chai'
  2. import { session, BrowserWindow, ipcMain } from 'electron'
  3. import { closeAllWindows } from './window-helpers'
  4. import * as http from 'http'
  5. import { AddressInfo } from 'net'
  6. import * as path from 'path'
  7. import * as fs from 'fs'
  8. import { ifdescribe } from './spec-helpers'
  9. import { emittedOnce } from './events-helpers'
  10. import { closeWindow } from './window-helpers';
  11. const fixtures = path.join(__dirname, 'fixtures')
  12. ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome extensions', () => {
  13. // NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default.
  14. let server: http.Server
  15. let url: string
  16. before(async () => {
  17. server = http.createServer((req, res) => res.end())
  18. await new Promise(resolve => server.listen(0, '127.0.0.1', () => {
  19. url = `http://127.0.0.1:${(server.address() as AddressInfo).port}`
  20. resolve()
  21. }))
  22. })
  23. after(() => {
  24. server.close()
  25. })
  26. afterEach(closeAllWindows)
  27. it('loads an extension', async () => {
  28. // NB. we have to use a persist: session (i.e. non-OTR) because the
  29. // extension registry is redirected to the main session. so installing an
  30. // extension in an in-memory session results in it being installed in the
  31. // default session.
  32. const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
  33. (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'red-bg'));
  34. const w = new BrowserWindow({show: false, webPreferences: {session: customSession}})
  35. await w.loadURL(url);
  36. const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
  37. expect(bg).to.equal('red');
  38. });
  39. it('confines an extension to the session it was loaded in', async () => {
  40. const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
  41. (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'red-bg'))
  42. const w = new BrowserWindow({show: false}) // not in the session
  43. await w.loadURL(url)
  44. const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor')
  45. expect(bg).to.equal('')
  46. })
  47. describe('chrome.runtime', () => {
  48. let content: any
  49. before(async () => {
  50. const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
  51. (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'chrome-runtime'))
  52. const w = new BrowserWindow({show: false, webPreferences: { session: customSession }})
  53. try {
  54. await w.loadURL(url)
  55. content = JSON.parse(await w.webContents.executeJavaScript('document.documentElement.textContent'))
  56. expect(content).to.be.an('object')
  57. } finally {
  58. w.destroy()
  59. }
  60. })
  61. it('getManifest()', () => {
  62. expect(content.manifest).to.be.an('object').with.property('name', 'chrome-runtime')
  63. })
  64. it('id', () => {
  65. expect(content.id).to.be.a('string').with.lengthOf(32)
  66. })
  67. it('getURL()', () => {
  68. expect(content.url).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/)
  69. })
  70. })
  71. describe('chrome.storage', () => {
  72. it('stores and retrieves a key', async () => {
  73. const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
  74. (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'chrome-storage'))
  75. const w = new BrowserWindow({show: false, webPreferences: { session: customSession, nodeIntegration: true }})
  76. try {
  77. const p = emittedOnce(ipcMain, 'storage-success')
  78. await w.loadURL(url)
  79. const [, v] = await p
  80. expect(v).to.equal('value')
  81. } finally {
  82. w.destroy()
  83. }
  84. })
  85. })
  86. })
  87. ifdescribe(!process.electronBinding('features').isExtensionsEnabled())('chrome extensions', () => {
  88. const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures')
  89. let w: BrowserWindow
  90. before(() => {
  91. BrowserWindow.addExtension(path.join(fixtures, 'extensions/chrome-api'))
  92. })
  93. after(() => {
  94. BrowserWindow.removeExtension('chrome-api')
  95. })
  96. beforeEach(() => {
  97. w = new BrowserWindow({ show: false })
  98. })
  99. afterEach(() => closeWindow(w).then(() => { w = null as unknown as BrowserWindow }))
  100. it('chrome.runtime.connect parses arguments properly', async function () {
  101. await w.loadURL('about:blank')
  102. const promise = emittedOnce(w.webContents, 'console-message')
  103. const message = { method: 'connect' }
  104. w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`)
  105. const [,, responseString] = await promise
  106. const response = JSON.parse(responseString)
  107. expect(response).to.be.true()
  108. })
  109. it('runtime.getManifest returns extension manifest', async () => {
  110. const actualManifest = (() => {
  111. const data = fs.readFileSync(path.join(fixtures, 'extensions/chrome-api/manifest.json'), 'utf-8')
  112. return JSON.parse(data)
  113. })()
  114. await w.loadURL('about:blank')
  115. const promise = emittedOnce(w.webContents, 'console-message')
  116. const message = { method: 'getManifest' }
  117. w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`)
  118. const [,, manifestString] = await promise
  119. const manifest = JSON.parse(manifestString)
  120. expect(manifest.name).to.equal(actualManifest.name)
  121. expect(manifest.content_scripts).to.have.lengthOf(actualManifest.content_scripts.length)
  122. })
  123. it('chrome.tabs.sendMessage receives the response', async function () {
  124. await w.loadURL('about:blank')
  125. const promise = emittedOnce(w.webContents, 'console-message')
  126. const message = { method: 'sendMessage', args: ['Hello World!'] }
  127. w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`)
  128. const [,, responseString] = await promise
  129. const response = JSON.parse(responseString)
  130. expect(response.message).to.equal('Hello World!')
  131. expect(response.tabId).to.equal(w.webContents.id)
  132. })
  133. it('chrome.tabs.executeScript receives the response', async function () {
  134. await w.loadURL('about:blank')
  135. const promise = emittedOnce(w.webContents, 'console-message')
  136. const message = { method: 'executeScript', args: ['1 + 2'] }
  137. w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`)
  138. const [,, responseString] = await promise
  139. const response = JSON.parse(responseString)
  140. expect(response).to.equal(3)
  141. })
  142. })