Skip to content

Commit

Permalink
feat: add tests and output the provider
Browse files Browse the repository at this point in the history
  • Loading branch information
fsdiogo committed Jun 7, 2019
1 parent 30f7e0b commit eb80656
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 161 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "src/index.js",
"scripts": {
"lint": "standard",
"test": "jest"
"test": "jest --verbose"
},
"repository": {
"type": "git",
Expand Down
10 changes: 5 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,31 @@ async function getIpfs (opts) {

if (opts.tryCompanion) {
const res = await tryCompanion({ root, ipfsConnectionTest })
if (res) return res.ipfs
if (res) return res
}

if (opts.tryWindow) {
const res = await tryWindow({ root, ipfsConnectionTest })
if (res) return res.ipfs
if (res) return res
}

if (opts.tryApi) {
const { apiAddress, defaultApiAddress } = opts
const { location } = root
const res = await tryApi({ apiAddress, defaultApiAddress, location, IpfsApi, ipfsConnectionTest })
if (res) return res.ipfs
if (res) return res
}

if (opts.tryJsIpfs) {
const { getJsIpfs, jsIpfsOpts } = opts
const res = await tryJsIpfs({ jsIpfsOpts, getJsIpfs, ipfsConnectionTest })
if (res) return res.ipfs
if (res) return res
}
}

function validateProvidedApiAddress (address) {
if (address && !isMultiaddress(address)) {
console.warn(`The ipfsApi address ${address} is invalid.`)
// `address` is not a valid multiaddr
return null
}
return address
Expand Down
9 changes: 2 additions & 7 deletions src/providers/ipfs-companion.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const PROVIDERS = require('../constants/providers')

async function tryCompanion ({ root, ipfsConnectionTest }) {
console.info('Trying IPFS Companion')
// Opportunistic optimizations when running from ipfs-companion (+ ipfs-desktop in future)
if (typeof root.chrome === 'object' && root.chrome.extension && root.chrome.extension.getBackgroundPage) {
// Note: under some vendors getBackgroundPage() will return null if window is in incognito mode
Expand All @@ -18,12 +17,8 @@ async function tryCompanion ({ root, ipfsConnectionTest }) {
// If extension is ipfs-companion exposing IPFS API, use it directly for best performance
if (webExt && webExt.ipfsCompanion && webExt.ipfsCompanion.ipfs) {
const ipfs = webExt.ipfsCompanion.ipfs
try {
await ipfsConnectionTest(ipfs)
return { ipfs, provider: PROVIDERS.companion }
} catch (error) {
console.warn('IPFS Companion detected but connection failed. Ignoring.', error)
}
await ipfsConnectionTest(ipfs)
return { ipfs, provider: PROVIDERS.companion }
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions src/providers/ipfs-http-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,25 @@ const PROVIDERS = require('../constants/providers')
async function tryApi ({ IpfsApi, apiAddress, defaultApiAddress, location, ipfsConnectionTest }) {
// Explicit custom apiAddress provided. Only try that.
if (apiAddress) {
console.info('Trying ipfs-api with custom api address', apiAddress)
return maybeApi({ apiAddress, ipfsConnectionTest, IpfsApi })
}

// Current origin is not localhost:5001 so try with current origin info
if (location.port !== '5001' || !location.hostname.match(/^127.0.0.1$|^localhost$/)) {
let originAddress = null

try {
originAddress = toMultiaddr(location.origin).toString()
} catch (err) {
console.warn(`Failed to convert ${location.origin} to a multiaddr`)
// Failed to convert `location.origin` to a multiaddr
}

if (originAddress) {
console.info('Trying ipfs-api at current origin', originAddress)
const res = await maybeApi({
apiAddress: originAddress,
apiOpts: {
protocol: location.protocol.slice(0, -1)
},
ipfsConnectionTest,
IpfsApi
})
Expand All @@ -34,7 +37,6 @@ async function tryApi ({ IpfsApi, apiAddress, defaultApiAddress, location, ipfsC
}

// ...otherwise try /ip4/127.0.0.1/tcp/5001
console.info('Trying ipfs-api', defaultApiAddress)
return maybeApi({ apiAddress: defaultApiAddress, ipfsConnectionTest, IpfsApi })
}

Expand All @@ -45,7 +47,7 @@ async function maybeApi ({ apiAddress, ipfsConnectionTest, IpfsApi }) {
await ipfsConnectionTest(ipfs)
return { ipfs, provider: PROVIDERS.api, apiAddress }
} catch (error) {
console.warn('Failed to connect to ipfs-api', apiAddress)
// Failed to connect to ipfs-api in `apiAddress`
}
}

Expand Down
17 changes: 6 additions & 11 deletions src/providers/js-ipfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
const PROVIDERS = require('../constants/providers')

function promiseMeJsIpfs (Ipfs, opts) {
// Allow the use of `import` or `require` on `getJsIpfs` fn
Ipfs = Ipfs.default || Ipfs
return new Promise((resolve, reject) => {
const ipfs = new Ipfs(opts)
ipfs.once('ready', () => resolve(ipfs))
Expand All @@ -11,17 +13,10 @@ function promiseMeJsIpfs (Ipfs, opts) {
}

async function tryJsIpfs ({ ipfsConnectionTest, getJsIpfs, jsIpfsOpts, initJsIpfs = promiseMeJsIpfs }) {
try {
console.info('Fetching js-ipfs')
const Ipfs = await getJsIpfs()
console.info('Trying js-ipfs', jsIpfsOpts)
const ipfs = await initJsIpfs(Ipfs, jsIpfsOpts)
await ipfsConnectionTest(ipfs)
console.info('js-ipfs ready!')
return { ipfs, provider: PROVIDERS.jsipfs }
} catch (error) {
console.warn('Failed to initialise js-ipfs', error)
}
const Ipfs = await getJsIpfs()
const ipfs = await initJsIpfs(Ipfs, jsIpfsOpts)
await ipfsConnectionTest(ipfs)
return { ipfs, provider: PROVIDERS.jsipfs }
}

module.exports = tryJsIpfs
12 changes: 2 additions & 10 deletions src/providers/window-ipfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,9 @@
const PROVIDERS = require('../constants/providers')

async function tryWindow ({ root, ipfsConnectionTest }) {
console.info('Trying window.ipfs')
if (root.ipfs) {
try {
await ipfsConnectionTest(root.ipfs)
console.info('Found `window.ipfs`. Nice!')
return { ipfs: root.ipfs, provider: PROVIDERS.window }
} catch (error) {
console.warn('Failed to connect via window.ipfs', error)
}
} else {
console.info('window.ipfs not found. Consider Installing the IPFS Companion web extension - https://github.com/ipfs-shipyard/ipfs-companion')
await ipfsConnectionTest(root.ipfs)
return { ipfs: root.ipfs, provider: PROVIDERS.window }
}
}

Expand Down
167 changes: 46 additions & 121 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,136 +8,61 @@ const tryJsIpfs = require('../src/providers/js-ipfs.js')
const PROVIDERS = require('../src/constants/providers.js')
const getIpfs = require('../src/index.js')

describe('provider: ipfs-companion', () => {
it('should connect to ipfs-companion', async () => {
// chrome.extension.getBackgroundPage().ipfsCompanion.ipfs will be present
// only if page was loaded from a path that belongs to our browser extension
const mockIpfs = {}
const root = {
chrome: {
extension: {
getBackgroundPage () {
return {
ipfsCompanion: {
ipfs: mockIpfs
}
}
}
}
}
}
const ipfsConnectionTest = (ipfs) => {
expect(ipfs).toEqual(mockIpfs)
Promise.resolve()
}
const res = await tryCompanion({ root, ipfsConnectionTest })
expect(res.ipfs).toEqual(mockIpfs)
expect(res.provider).toEqual(PROVIDERS.companion)
})
})

describe('provider: window.ipfs', () => {
it('should connect to window.ipfs', async () => {
const opts = {
root: {
ipfs: {}
},
ipfsConnectionTest: jest.fn().mockResolvedValueOnce(true)
}
const res = await tryWindow(opts)
expect(res.ipfs).toEqual(opts.root.ipfs)
expect(res.provider).toEqual(PROVIDERS.window)
expect(opts.ipfsConnectionTest.mock.calls.length).toBe(1)
})
})
jest.mock('../src/providers/ipfs-companion.js')
jest.mock('../src/providers/window-ipfs.js')
jest.mock('../src/providers/ipfs-http-api.js')
jest.mock('../src/providers/js-ipfs.js')

describe('provider: ipfs-http-api', () => {
it('should use the apiAddress', async () => {
const opts = {
apiAddress: '/ip4/1.1.1.1/tcp/1111',
defaultApiAddress: '/ip4/127.0.0.1/tcp/5001',
location: new URL('http://localhost:5001'),
IpfsApi: jest.fn(),
ipfsConnectionTest: jest.fn().mockResolvedValueOnce(true)
}
const res = await tryApi(opts)
expect(res.apiAddress).toEqual(opts.apiAddress)
expect(res.provider).toEqual(PROVIDERS.api)
expect(opts.ipfsConnectionTest.mock.calls.length).toBe(1)
expect(opts.IpfsApi.mock.calls.length).toBe(1)
})

it('should use the location where hostname not localhost', async () => {
const opts = {
defaultApiAddress: '/ip4/127.0.0.1/tcp/5001',
location: new URL('http://dev.local:5001'),
IpfsApi: jest.fn(),
ipfsConnectionTest: jest.fn().mockResolvedValueOnce(true)
}
const res = await tryApi(opts)
expect(res.apiAddress).toEqual('/dns4/dev.local/tcp/5001/http')
expect(res.provider).toEqual(PROVIDERS.api)
expect(opts.ipfsConnectionTest.mock.calls.length).toBe(1)
expect(opts.IpfsApi.mock.calls.length).toBe(1)
describe('getIpfs via availabe providers', () => {
it('should try nothing and fail if all providers are disabled', async () => {
const res = await getIpfs({
tryCompanion: false,
tryWindow: false,
tryApi: false,
tryJsIpfs: false
})
expect(res).toBeFalsy()
})

it('should use the location where port not 5001', async () => {
const opts = {
defaultApiAddress: '/ip4/127.0.0.1/tcp/5001',
location: new URL('http://localhost:9999'),
IpfsApi: jest.fn(),
ipfsConnectionTest: jest.fn().mockResolvedValueOnce(true)
}
const res = await tryApi(opts)
expect(res.apiAddress).toEqual('/dns4/localhost/tcp/9999/http')
expect(res.provider).toEqual(PROVIDERS.api)
expect(opts.ipfsConnectionTest.mock.calls.length).toBe(1)
expect(opts.IpfsApi.mock.calls.length).toBe(1)
it('should try ipfs-companion first', async () => {
const mockResult = { ipfs: {}, provider: PROVIDERS.companion }
tryCompanion.mockResolvedValue(mockResult)
tryWindow.mockResolvedValue({ ipfs: {}, provider: 'nope' })
const { ipfs, provider } = await getIpfs()
expect(ipfs).toBeTruthy()
expect(provider).toBe(mockResult.provider)
})

it('should use the defaultApiAddress if location fails', async () => {
const opts = {
defaultApiAddress: '/ip4/127.0.0.1/tcp/5001',
location: new URL('http://astro.cat:5001'),
IpfsApi: jest.fn(),
// location call fails, default ok
ipfsConnectionTest: jest.fn()
.mockRejectedValueOnce(new Error('nope'))
.mockResolvedValueOnce(true)
}
const res = await tryApi(opts)
expect(res.apiAddress).toEqual(opts.defaultApiAddress)
expect(res.provider).toEqual(PROVIDERS.api)
expect(opts.ipfsConnectionTest.mock.calls.length).toBe(2)
expect(opts.IpfsApi.mock.calls.length).toBe(2)
it('should try window.ipfs after companion', async () => {
const mockResult = { ipfs: {}, provider: PROVIDERS.window }
tryCompanion.mockResolvedValue(null)
tryWindow.mockResolvedValue(mockResult)
const { ipfs, provider } = await getIpfs()
expect(ipfs).toBeTruthy()
expect(provider).toBe(mockResult.provider)
})
})

describe('provider: js-ipfs', () => {
it('should connect to js-ipfs', async () => {
const mockIpfs = {}
const opts = {
ipfsConnectionTest: jest.fn().mockResolvedValueOnce(true),
getJsIpfs: jest.fn().mockResolvedValueOnce(true),
jsIpfsOpts: {},
initJsIpfs: jest.fn().mockResolvedValue(mockIpfs)
}
const res = await tryJsIpfs(opts)
expect(res.ipfs).toEqual(mockIpfs)
expect(res.provider).toEqual(PROVIDERS.jsipfs)
expect(opts.ipfsConnectionTest.mock.calls.length).toBe(1)
expect(opts.initJsIpfs.mock.calls.length).toBe(1)
it('should try ipfs-http-api after window.ipfs', async () => {
const mockResult = { ipfs: {}, provider: PROVIDERS.api }
tryCompanion.mockResolvedValue(null)
tryWindow.mockResolvedValue(null)
tryApi.mockResolvedValue(mockResult)
const { ipfs, provider } = await getIpfs()
expect(ipfs).toBeTruthy()
expect(provider).toBe(mockResult.provider)
})
})

describe('getIpfs via providers', () => {
it('should try nothing and fail if all providers are disabled', async () => {
const res = await getIpfs({
tryCompanion: false,
tryWindow: false,
tryApi: false,
tryJsIpfs: false
it('should try js-ipfs if enabled', async () => {
const mockResult = { ipfs: {}, provider: PROVIDERS.jsipfs }
tryCompanion.mockResolvedValue(null)
tryWindow.mockResolvedValue(null)
tryApi.mockResolvedValue(null)
tryJsIpfs.mockResolvedValue(mockResult)
const { ipfs, provider } = await getIpfs({
tryJsIpfs: true,
getJsIpfs: jest.fn()
})
expect(res).toBe(undefined)
expect(ipfs).toBeTruthy()
expect(provider).toBe(mockResult.provider)
})
})
Loading

0 comments on commit eb80656

Please sign in to comment.