mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-22 18:03:30 +01:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4fcdff24 | ||
|
|
91cc450451 | ||
|
|
c1815e5e48 | ||
|
|
287e203449 | ||
|
|
92be208a35 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 16.x
|
node-version: 16.6.x
|
||||||
-
|
-
|
||||||
run: yarn install
|
run: yarn install
|
||||||
-
|
-
|
||||||
|
|||||||
@@ -49,5 +49,6 @@ COPY src/Sonoswsdl-1.19.4-20190411.142401-3.wsdl ./src/Sonoswsdl-1.19.4-20190411
|
|||||||
RUN apk add --no-cache --update vips
|
RUN apk add --no-cache --update vips
|
||||||
|
|
||||||
USER nobody
|
USER nobody
|
||||||
|
WORKDIR /bonob/src
|
||||||
|
|
||||||
CMD ["node", "/bonob/src/app.js"]
|
CMD ["node", "app.js"]
|
||||||
12
README.md
12
README.md
@@ -79,6 +79,8 @@ docker run \
|
|||||||
|
|
||||||
Now within the LAN that contains the sonos devices run bonob the registration process.
|
Now within the LAN that contains the sonos devices run bonob the registration process.
|
||||||
|
|
||||||
|
#### Using auto-discovery
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run \
|
docker run \
|
||||||
--rm \
|
--rm \
|
||||||
@@ -86,6 +88,16 @@ docker run \
|
|||||||
simojenki/bonob register https://my-server.example.com/bonob
|
simojenki/bonob register https://my-server.example.com/bonob
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Using a seed host
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run \
|
||||||
|
--rm \
|
||||||
|
-e BONOB_SONOS_SEED_HOST=192.168.1.163 \
|
||||||
|
simojenki/bonob register https://my-server.example.com/bonob
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Running bonob and navidrome using docker-compose
|
### Running bonob and navidrome using docker-compose
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
62
package.json
62
package.json
@@ -6,49 +6,49 @@
|
|||||||
"author": "simojenki <simojenki@users.noreply.github.com>",
|
"author": "simojenki <simojenki@users.noreply.github.com>",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svrooij/sonos": "^2.3.0",
|
"@svrooij/sonos": "^2.4.0",
|
||||||
"@types/express": "^4.17.11",
|
"@types/express": "^4.17.13",
|
||||||
"@types/morgan": "^1.9.2",
|
"@types/morgan": "^1.9.3",
|
||||||
"@types/node": "^14.14.22",
|
"@types/node": "^16.7.13",
|
||||||
"@types/sharp": "^0.27.1",
|
"@types/sharp": "^0.28.6",
|
||||||
"@types/underscore": "1.10.24",
|
"@types/underscore": "^1.11.3",
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.1",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.4",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.6",
|
||||||
"eta": "^1.12.1",
|
"eta": "^1.12.3",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"fp-ts": "^2.9.5",
|
"fp-ts": "^2.11.1",
|
||||||
"libxmljs2": "^0.27.0",
|
"libxmljs2": "^0.28.0",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"node-html-parser": "^2.1.0",
|
"node-html-parser": "^4.1.4",
|
||||||
"sharp": "^0.27.2",
|
"sharp": "^0.29.1",
|
||||||
"soap": "^0.37.0",
|
"soap": "^0.42.0",
|
||||||
"ts-md5": "^1.2.7",
|
"ts-md5": "^1.2.9",
|
||||||
"typescript": "^4.1.3",
|
"typescript": "^4.4.2",
|
||||||
"underscore": "^1.12.1",
|
"underscore": "^1.13.1",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"winston": "^3.3.3",
|
"winston": "^3.3.3",
|
||||||
"x2js": "^3.4.1"
|
"x2js": "^3.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.2.14",
|
"@types/chai": "^4.2.21",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^27.0.1",
|
||||||
"@types/mocha": "^8.2.0",
|
"@types/mocha": "^9.0.0",
|
||||||
"@types/supertest": "^2.0.10",
|
"@types/supertest": "^2.0.11",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.3.4",
|
||||||
"get-port": "^5.1.1",
|
"get-port": "^5.1.1",
|
||||||
"image-js": "^0.32.0",
|
"image-js": "^0.33.0",
|
||||||
"jest": "^26.6.3",
|
"jest": "^27.1.0",
|
||||||
"nodemon": "^2.0.7",
|
"nodemon": "^2.0.12",
|
||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.6",
|
||||||
"ts-jest": "^26.4.4",
|
"ts-jest": "^27.0.5",
|
||||||
"ts-mockito": "^2.6.1",
|
"ts-mockito": "^2.6.1",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^10.2.1",
|
||||||
"xmldom-ts": "^0.3.1",
|
"xmldom-ts": "^0.3.1",
|
||||||
"xpath-ts": "^1.3.13"
|
"xpath-ts": "^1.3.13"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "rm -Rf build",
|
"clean": "rm -Rf build node_modules",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"dev": "BONOB_ICON_FOREGROUND_COLOR=white BONOB_ICON_BACKGROUND_COLOR=darkgrey BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true nodemon -V ./src/app.ts",
|
"dev": "BONOB_ICON_FOREGROUND_COLOR=white BONOB_ICON_BACKGROUND_COLOR=darkgrey BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true nodemon -V ./src/app.ts",
|
||||||
"devr": "BONOB_ICON_FOREGROUND_COLOR=white BONOB_ICON_BACKGROUND_COLOR=darkgrey BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true BONOB_SONOS_AUTO_REGISTER=true nodemon -V ./src/app.ts",
|
"devr": "BONOB_ICON_FOREGROUND_COLOR=white BONOB_ICON_BACKGROUND_COLOR=darkgrey BONOB_SONOS_SERVICE_NAME=bonobDev BONOB_SONOS_DEVICE_DISCOVERY=true BONOB_SONOS_AUTO_REGISTER=true nodemon -V ./src/app.ts",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const bonob = bonobService(
|
|||||||
"AppLink"
|
"AppLink"
|
||||||
);
|
);
|
||||||
|
|
||||||
const sonosSystem = sonos(config.sonos.deviceDiscovery, config.sonos.seedHost);
|
const sonosSystem = sonos(config.sonos.discovery);
|
||||||
|
|
||||||
const streamUserAgent = config.navidrome.customClientsFor
|
const streamUserAgent = config.navidrome.customClientsFor
|
||||||
? appendMimeTypeToClientFor(config.navidrome.customClientsFor.split(","))
|
? appendMimeTypeToClientFor(config.navidrome.customClientsFor.split(","))
|
||||||
@@ -90,7 +90,7 @@ if (config.sonos.autoRegister) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if(config.sonos.deviceDiscovery) {
|
} else if(config.sonos.discovery.auto) {
|
||||||
sonosSystem.devices().then(devices => {
|
sonosSystem.devices().then(devices => {
|
||||||
devices.forEach(d => {
|
devices.forEach(d => {
|
||||||
logger.info(`Found device ${d.name}(${d.group}) @ ${d.ip}:${d.port}`)
|
logger.info(`Found device ${d.name}(${d.group}) @ ${d.ip}:${d.port}`)
|
||||||
|
|||||||
@@ -36,9 +36,11 @@ export default function () {
|
|||||||
},
|
},
|
||||||
sonos: {
|
sonos: {
|
||||||
serviceName: process.env["BONOB_SONOS_SERVICE_NAME"] || "bonob",
|
serviceName: process.env["BONOB_SONOS_SERVICE_NAME"] || "bonob",
|
||||||
deviceDiscovery:
|
discovery: {
|
||||||
|
auto:
|
||||||
(process.env["BONOB_SONOS_DEVICE_DISCOVERY"] || "true") == "true",
|
(process.env["BONOB_SONOS_DEVICE_DISCOVERY"] || "true") == "true",
|
||||||
seedHost: process.env["BONOB_SONOS_SEED_HOST"],
|
seedHost: process.env["BONOB_SONOS_SEED_HOST"],
|
||||||
|
},
|
||||||
autoRegister:
|
autoRegister:
|
||||||
(process.env["BONOB_SONOS_AUTO_REGISTER"] || "false") == "true",
|
(process.env["BONOB_SONOS_AUTO_REGISTER"] || "false") == "true",
|
||||||
sid: Number(process.env["BONOB_SONOS_SERVICE_ID"] || "246"),
|
sid: Number(process.env["BONOB_SONOS_SERVICE_ID"] || "246"),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import registrar from "./registrar";
|
import registrar from "./registrar";
|
||||||
|
import readConfig from "./config";
|
||||||
import { URLBuilder } from "./url_builder";
|
import { URLBuilder } from "./url_builder";
|
||||||
|
|
||||||
const params = process.argv.slice(2);
|
const params = process.argv.slice(2);
|
||||||
@@ -9,7 +10,10 @@ if (params.length != 1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bonobUrl = new URLBuilder(params[0]!);
|
const bonobUrl = new URLBuilder(params[0]!);
|
||||||
registrar(bonobUrl)()
|
|
||||||
|
const config = readConfig();
|
||||||
|
|
||||||
|
registrar(bonobUrl, config.sonos.discovery)()
|
||||||
.then((success) => {
|
.then((success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
console.log(`Successfully registered bonob @ ${bonobUrl} with sonos`);
|
console.log(`Successfully registered bonob @ ${bonobUrl} with sonos`);
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import _ from "underscore";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import sonos, { bonobService } from "./sonos";
|
import sonos, { bonobService, Discovery } from "./sonos";
|
||||||
import { URLBuilder } from "./url_builder";
|
import { URLBuilder } from "./url_builder";
|
||||||
|
|
||||||
export default (bonobUrl: URLBuilder) => async () => {
|
export default (
|
||||||
|
bonobUrl: URLBuilder,
|
||||||
|
sonosDiscovery: Discovery = {
|
||||||
|
auto: true,
|
||||||
|
seedHost: undefined,
|
||||||
|
}
|
||||||
|
) =>
|
||||||
|
async () => {
|
||||||
const about = bonobUrl.append({ pathname: "/about" });
|
const about = bonobUrl.append({ pathname: "/about" });
|
||||||
logger.info(`Fetching bonob service about from ${about}`);
|
logger.info(`Fetching bonob service about from ${about}`);
|
||||||
return axios
|
return axios
|
||||||
@@ -12,8 +20,19 @@ export default (bonobUrl: URLBuilder) => async () => {
|
|||||||
if (res.status == 200) return res.data;
|
if (res.status == 200) return res.data;
|
||||||
else throw `Unexpected response status ${res.status} from ${about}`;
|
else throw `Unexpected response status ${res.status} from ${about}`;
|
||||||
})
|
})
|
||||||
.then((about) =>
|
.then((res) => {
|
||||||
bonobService(about.service.name, about.service.sid, bonobUrl)
|
const name = _.get(res, ["service", "name"]);
|
||||||
)
|
const sid = _.get(res, ["service", "sid"]);
|
||||||
.then((bonobService) => sonos(true).register(bonobService));
|
if (!name || !sid) {
|
||||||
|
throw `Unexpected response from ${about.href()}, expected service.name and service.sid`;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
sid: Number.parseInt(sid),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.then(({ name, sid }: { name: string; sid: number }) =>
|
||||||
|
bonobService(name, sid, bonobUrl)
|
||||||
|
)
|
||||||
|
.then((service) => sonos(sonosDiscovery).register(service));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ export const album = (bonobUrl: URLBuilder, album: AlbumSummary) => ({
|
|||||||
itemType: "album",
|
itemType: "album",
|
||||||
id: `album:${album.id}`,
|
id: `album:${album.id}`,
|
||||||
artist: album.artistName,
|
artist: album.artistName,
|
||||||
artistId: album.artistId,
|
artistId: `artist:${album.artistId}`,
|
||||||
title: album.name,
|
title: album.name,
|
||||||
albumArtURI: defaultAlbumArtURI(bonobUrl, album).href(),
|
albumArtURI: defaultAlbumArtURI(bonobUrl, album).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
|
|||||||
46
src/sonos.ts
46
src/sonos.ts
@@ -9,7 +9,20 @@ import qs from "querystring";
|
|||||||
import { URLBuilder } from "./url_builder";
|
import { URLBuilder } from "./url_builder";
|
||||||
import { LANG } from "./i8n";
|
import { LANG } from "./i8n";
|
||||||
|
|
||||||
export const SONOS_LANG: LANG[] = ["en-US", "da-DK", "de-DE", "es-ES", "fr-FR", "it-IT", "ja-JP", "nb-NO", "nl-NL", "pt-BR", "sv-SE", "zh-CN"]
|
export const SONOS_LANG: LANG[] = [
|
||||||
|
"en-US",
|
||||||
|
"da-DK",
|
||||||
|
"de-DE",
|
||||||
|
"es-ES",
|
||||||
|
"fr-FR",
|
||||||
|
"it-IT",
|
||||||
|
"ja-JP",
|
||||||
|
"nb-NO",
|
||||||
|
"nl-NL",
|
||||||
|
"pt-BR",
|
||||||
|
"sv-SE",
|
||||||
|
"zh-CN",
|
||||||
|
];
|
||||||
|
|
||||||
export const PRESENTATION_AND_STRINGS_VERSION = "21";
|
export const PRESENTATION_AND_STRINGS_VERSION = "21";
|
||||||
|
|
||||||
@@ -118,7 +131,7 @@ export const asDevice = (sonosDevice: SonosDevice): Device => ({
|
|||||||
|
|
||||||
export const asRemoveCustomdForm = (csrfToken: string, sid: number) => ({
|
export const asRemoveCustomdForm = (csrfToken: string, sid: number) => ({
|
||||||
csrfToken,
|
csrfToken,
|
||||||
sid: `${sid}`
|
sid: `${sid}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const asCustomdForm = (csrfToken: string, service: Service) => ({
|
export const asCustomdForm = (csrfToken: string, service: Service) => ({
|
||||||
@@ -168,7 +181,10 @@ export function autoDiscoverySonos(sonosSeedHost?: string): Sonos {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const post = async (action: string, customdForm: (csrfToken: string) => any) => {
|
const post = async (
|
||||||
|
action: string,
|
||||||
|
customdForm: (csrfToken: string) => any
|
||||||
|
) => {
|
||||||
const anyDevice = await sonosDevices().then((devices) => head(devices));
|
const anyDevice = await sonosDevices().then((devices) => head(devices));
|
||||||
|
|
||||||
if (!anyDevice) {
|
if (!anyDevice) {
|
||||||
@@ -195,7 +211,7 @@ export function autoDiscoverySonos(sonosSeedHost?: string): Sonos {
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const form = customdForm(csrfToken)
|
const form = customdForm(csrfToken);
|
||||||
logger.info(`${action} with sonos @ ${customd}`, { form });
|
logger.info(`${action} with sonos @ ${customd}`, { form });
|
||||||
return axios
|
return axios
|
||||||
.post(customd, new URLSearchParams(qs.stringify(form)), {
|
.post(customd, new URLSearchParams(qs.stringify(form)), {
|
||||||
@@ -218,16 +234,22 @@ export function autoDiscoverySonos(sonosSeedHost?: string): Sonos {
|
|||||||
)
|
)
|
||||||
.then((it) => it.map(asService)),
|
.then((it) => it.map(asService)),
|
||||||
|
|
||||||
remove: async (sid: number) => post("remove", (csrfToken) => asRemoveCustomdForm(csrfToken, sid)),
|
remove: async (sid: number) =>
|
||||||
|
post("remove", (csrfToken) => asRemoveCustomdForm(csrfToken, sid)),
|
||||||
|
|
||||||
register: async (service: Service) => post("register", (csrfToken) => asCustomdForm(csrfToken, service)),
|
register: async (service: Service) =>
|
||||||
|
post("register", (csrfToken) => asCustomdForm(csrfToken, service)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const sonos = (
|
export type Discovery = {
|
||||||
discoveryEnabled: boolean = true,
|
auto: boolean;
|
||||||
sonosSeedHost: string | undefined = undefined
|
seedHost?: string;
|
||||||
): Sonos =>
|
};
|
||||||
discoveryEnabled ? autoDiscoverySonos(sonosSeedHost) : SONOS_DISABLED;
|
|
||||||
|
|
||||||
export default sonos;
|
export default (
|
||||||
|
sonosDiscovery: Discovery = { auto: true }
|
||||||
|
): Sonos =>
|
||||||
|
sonosDiscovery.auto
|
||||||
|
? autoDiscoverySonos(sonosDiscovery.seedHost)
|
||||||
|
: SONOS_DISABLED;
|
||||||
|
|||||||
@@ -198,17 +198,17 @@ describe("config", () => {
|
|||||||
"deviceDiscovery",
|
"deviceDiscovery",
|
||||||
"BONOB_SONOS_DEVICE_DISCOVERY",
|
"BONOB_SONOS_DEVICE_DISCOVERY",
|
||||||
true,
|
true,
|
||||||
(config) => config.sonos.deviceDiscovery
|
(config) => config.sonos.discovery.auto
|
||||||
);
|
);
|
||||||
|
|
||||||
describe("seedHost", () => {
|
describe("seedHost", () => {
|
||||||
it("should default to undefined", () => {
|
it("should default to undefined", () => {
|
||||||
expect(config().sonos.seedHost).toBeUndefined();
|
expect(config().sonos.discovery.seedHost).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be overridable", () => {
|
it("should be overridable", () => {
|
||||||
process.env["BONOB_SONOS_SEED_HOST"] = "123.456.789.0";
|
process.env["BONOB_SONOS_SEED_HOST"] = "123.456.789.0";
|
||||||
expect(config().sonos.seedHost).toEqual("123.456.789.0");
|
expect(config().sonos.discovery.seedHost).toEqual("123.456.789.0");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import sharp from "sharp";
|
|||||||
jest.mock("sharp");
|
jest.mock("sharp");
|
||||||
|
|
||||||
import randomString from "../src/random_string";
|
import randomString from "../src/random_string";
|
||||||
|
jest.mock("../src/random_string");
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Album,
|
Album,
|
||||||
Artist,
|
Artist,
|
||||||
@@ -43,8 +45,6 @@ import {
|
|||||||
aTrack,
|
aTrack,
|
||||||
} from "./builders";
|
} from "./builders";
|
||||||
|
|
||||||
jest.mock("../src/random_string");
|
|
||||||
|
|
||||||
describe("t", () => {
|
describe("t", () => {
|
||||||
it("should be an md5 of the password and the salt", () => {
|
it("should be an md5 of the password and the salt", () => {
|
||||||
const p = "password123";
|
const p = "password123";
|
||||||
|
|||||||
116
tests/registrar.test.ts
Normal file
116
tests/registrar.test.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
jest.mock("axios");
|
||||||
|
|
||||||
|
const fakeSonos = {
|
||||||
|
register: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
import sonos, { bonobService } from "../src/sonos";
|
||||||
|
jest.mock("../src/sonos");
|
||||||
|
|
||||||
|
import registrar from "../src/registrar";
|
||||||
|
import { URLBuilder } from "../src/url_builder";
|
||||||
|
|
||||||
|
describe("registrar", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the bonob service can not be found", () => {
|
||||||
|
it("should fail", async () => {
|
||||||
|
const status = 409;
|
||||||
|
|
||||||
|
(axios.get as jest.Mock).mockResolvedValue({
|
||||||
|
status,
|
||||||
|
});
|
||||||
|
|
||||||
|
const bonobUrl = new URLBuilder("http://fail.example.com/bonob");
|
||||||
|
|
||||||
|
return expect(registrar(bonobUrl)()).rejects.toEqual(
|
||||||
|
`Unexpected response status ${status} from ${bonobUrl
|
||||||
|
.append({ pathname: "/about" })
|
||||||
|
.href()}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the bonob service returns unexpected content", () => {
|
||||||
|
it("should fail", async () => {
|
||||||
|
(axios.get as jest.Mock).mockResolvedValue({
|
||||||
|
status: 200,
|
||||||
|
// invalid response from /about as does not have name and sid
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bonobUrl = new URLBuilder("http://fail.example.com/bonob");
|
||||||
|
|
||||||
|
return expect(registrar(bonobUrl)()).rejects.toEqual(
|
||||||
|
`Unexpected response from ${bonobUrl
|
||||||
|
.append({ pathname: "/about" })
|
||||||
|
.href()}, expected service.name and service.sid`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the bonob service can be found", () => {
|
||||||
|
const bonobUrl = new URLBuilder("http://success.example.com/bonob");
|
||||||
|
|
||||||
|
const serviceDetails = {
|
||||||
|
name: "bob",
|
||||||
|
sid: 123,
|
||||||
|
};
|
||||||
|
|
||||||
|
const service = "service";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(axios.get as jest.Mock).mockResolvedValue({
|
||||||
|
status: 200,
|
||||||
|
data: {
|
||||||
|
service: serviceDetails,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
(bonobService as jest.Mock).mockResolvedValue(service);
|
||||||
|
(sonos as jest.Mock).mockReturnValue(fakeSonos);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when registration succeeds", () => {
|
||||||
|
it("should fetch the service details and register", async () => {
|
||||||
|
fakeSonos.register.mockResolvedValue(true);
|
||||||
|
const sonosDiscovery = { auto: true };
|
||||||
|
|
||||||
|
expect(await registrar(bonobUrl, sonosDiscovery)()).toEqual(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(bonobService).toHaveBeenCalledWith(
|
||||||
|
serviceDetails.name,
|
||||||
|
serviceDetails.sid,
|
||||||
|
bonobUrl
|
||||||
|
);
|
||||||
|
expect(sonos).toHaveBeenCalledWith(sonosDiscovery);
|
||||||
|
expect(fakeSonos.register).toHaveBeenCalledWith(service);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when registration fails", () => {
|
||||||
|
it("should fetch the service details and register", async () => {
|
||||||
|
fakeSonos.register.mockResolvedValue(false);
|
||||||
|
const sonosDiscovery = { auto: false, seedHost: "192.168.1.163" };
|
||||||
|
|
||||||
|
expect(await registrar(bonobUrl, sonosDiscovery)()).toEqual(
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(bonobService).toHaveBeenCalledWith(
|
||||||
|
serviceDetails.name,
|
||||||
|
serviceDetails.sid,
|
||||||
|
bonobUrl
|
||||||
|
);
|
||||||
|
expect(sonos).toHaveBeenCalledWith(sonosDiscovery);
|
||||||
|
expect(fakeSonos.register).toHaveBeenCalledWith(service);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -299,7 +299,7 @@ describe("album", () => {
|
|||||||
albumArtURI: defaultAlbumArtURI(bonobUrl, someAlbum).href(),
|
albumArtURI: defaultAlbumArtURI(bonobUrl, someAlbum).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artist: someAlbum.artistName,
|
artist: someAlbum.artistName,
|
||||||
artistId: someAlbum.artistId,
|
artistId: `artist:${someAlbum.artistId}`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1168,7 +1168,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
@@ -1205,7 +1205,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 2,
|
index: 2,
|
||||||
@@ -1509,7 +1509,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1557,7 +1557,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1605,7 +1605,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1653,7 +1653,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1701,7 +1701,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1747,7 +1747,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1793,7 +1793,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 2,
|
index: 2,
|
||||||
@@ -1837,7 +1837,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -1884,7 +1884,7 @@ describe("api", () => {
|
|||||||
it
|
it
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: it.artistId,
|
artistId: `artist:${it.artistId}`,
|
||||||
artist: it.artistName,
|
artist: it.artistName,
|
||||||
})),
|
})),
|
||||||
index: 0,
|
index: 0,
|
||||||
@@ -2344,7 +2344,7 @@ describe("api", () => {
|
|||||||
album
|
album
|
||||||
).href(),
|
).href(),
|
||||||
canPlay: true,
|
canPlay: true,
|
||||||
artistId: album.artistId,
|
artistId: `artist:${album.artistId}`,
|
||||||
artist: album.artistName,
|
artist: album.artistName,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ describe("sonos", () => {
|
|||||||
|
|
||||||
describe("when is disabled", () => {
|
describe("when is disabled", () => {
|
||||||
it("should return a disabled client", async () => {
|
it("should return a disabled client", async () => {
|
||||||
const disabled = sonos(false);
|
const disabled = sonos({ auto: false });
|
||||||
|
|
||||||
expect(disabled).toEqual(SONOS_DISABLED);
|
expect(disabled).toEqual(SONOS_DISABLED);
|
||||||
expect(await disabled.devices()).toEqual([]);
|
expect(await disabled.devices()).toEqual([]);
|
||||||
@@ -310,7 +310,7 @@ describe("sonos", () => {
|
|||||||
);
|
);
|
||||||
sonosManager.InitializeWithDiscovery.mockResolvedValue(true);
|
sonosManager.InitializeWithDiscovery.mockResolvedValue(true);
|
||||||
|
|
||||||
const actualDevices = await sonos(true, undefined).devices();
|
const actualDevices = await sonos({ auto: true }).devices();
|
||||||
|
|
||||||
expect(SonosManager).toHaveBeenCalledTimes(1);
|
expect(SonosManager).toHaveBeenCalledTimes(1);
|
||||||
expect(sonosManager.InitializeWithDiscovery).toHaveBeenCalledWith(10);
|
expect(sonosManager.InitializeWithDiscovery).toHaveBeenCalledWith(10);
|
||||||
@@ -331,7 +331,7 @@ describe("sonos", () => {
|
|||||||
);
|
);
|
||||||
sonosManager.InitializeWithDiscovery.mockResolvedValue(true);
|
sonosManager.InitializeWithDiscovery.mockResolvedValue(true);
|
||||||
|
|
||||||
const actualDevices = await sonos(true, "").devices();
|
const actualDevices = await sonos({ auto: true, seedHost: "" }).devices();
|
||||||
|
|
||||||
expect(SonosManager).toHaveBeenCalledTimes(1);
|
expect(SonosManager).toHaveBeenCalledTimes(1);
|
||||||
expect(sonosManager.InitializeWithDiscovery).toHaveBeenCalledWith(10);
|
expect(sonosManager.InitializeWithDiscovery).toHaveBeenCalledWith(10);
|
||||||
@@ -354,7 +354,7 @@ describe("sonos", () => {
|
|||||||
);
|
);
|
||||||
sonosManager.InitializeFromDevice.mockResolvedValue(true);
|
sonosManager.InitializeFromDevice.mockResolvedValue(true);
|
||||||
|
|
||||||
const actualDevices = await sonos(true, seedHost).devices();
|
const actualDevices = await sonos({ auto: true, seedHost }).devices();
|
||||||
|
|
||||||
expect(SonosManager).toHaveBeenCalledTimes(1);
|
expect(SonosManager).toHaveBeenCalledTimes(1);
|
||||||
expect(sonosManager.InitializeFromDevice).toHaveBeenCalledWith(
|
expect(sonosManager.InitializeFromDevice).toHaveBeenCalledWith(
|
||||||
@@ -377,7 +377,7 @@ describe("sonos", () => {
|
|||||||
);
|
);
|
||||||
sonosManager.InitializeWithDiscovery.mockResolvedValue(true);
|
sonosManager.InitializeWithDiscovery.mockResolvedValue(true);
|
||||||
|
|
||||||
const actualDevices = await sonos(true, undefined).devices();
|
const actualDevices = await sonos({ auto: true, seedHost: undefined }).devices();
|
||||||
|
|
||||||
expect(actualDevices).toEqual([
|
expect(actualDevices).toEqual([
|
||||||
{
|
{
|
||||||
@@ -408,7 +408,7 @@ describe("sonos", () => {
|
|||||||
);
|
);
|
||||||
sonosManager.InitializeWithDiscovery.mockResolvedValue(false);
|
sonosManager.InitializeWithDiscovery.mockResolvedValue(false);
|
||||||
|
|
||||||
expect(await sonos(true, "").devices()).toEqual([]);
|
expect(await sonos({ auto: true, seedHost: "" }).devices()).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Express } from "express";
|
import { Express } from "express";
|
||||||
|
import { ReadStream } from "fs";
|
||||||
|
import { IHttpClient } from "soap";
|
||||||
import request from "supertest";
|
import request from "supertest";
|
||||||
|
import * as req from "axios";
|
||||||
|
|
||||||
function supersoap(server: Express) {
|
function supersoap(server: Express): IHttpClient {
|
||||||
return {
|
return {
|
||||||
request: (
|
request: (
|
||||||
rurl: string,
|
rurl: string,
|
||||||
@@ -15,12 +18,19 @@ function supersoap(server: Express) {
|
|||||||
data == null
|
data == null
|
||||||
? request(server).get(withoutHost).send()
|
? request(server).get(withoutHost).send()
|
||||||
: request(server).post(withoutHost).send(data);
|
: request(server).post(withoutHost).send(data);
|
||||||
req
|
return req
|
||||||
.set(exheaders || {})
|
.set(exheaders || {})
|
||||||
.then((response) => callback(null, response, response.text))
|
.then((response) => callback(null, response, response.text))
|
||||||
.catch(callback);
|
.catch(callback);
|
||||||
},
|
},
|
||||||
}
|
|
||||||
|
requestStream: (
|
||||||
|
_: string,
|
||||||
|
_2: any
|
||||||
|
): req.AxiosPromise<ReadStream> => {
|
||||||
|
throw "Not Implemented!!";
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default supersoap
|
export default supersoap;
|
||||||
|
|||||||
Reference in New Issue
Block a user