Add/improve e2e test cases (#144)

https://github.com/gesellix/Bose-SoundTouch/issues/135
This commit is contained in:
Tobias Gesellchen
2026-04-04 21:05:34 +02:00
committed by GitHub
parent c1e7d513b4
commit 50e45ab5f2
13 changed files with 536 additions and 65 deletions
@@ -0,0 +1,13 @@
### DELETE /streaming/account/{{accountId}}/device/{{deviceId}}/preset/6
DELETE {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/preset/6
Host: streaming.bose.com
Accept: application/vnd.bose.streaming-v1.2+xml
Authorization: Bearer {{token}}
Content-Type: application/vnd.bose.streaming-v1.2+xml
User-Agent: Bose_Lisa/27.0.6
> {%
client.test("Response is 200 OK", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}
@@ -0,0 +1,65 @@
### GET /streaming/account/{{accountId}}/device/{{deviceId}}/presets
GET {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/presets
Host: streaming.bose.com
Accept: application/vnd.bose.streaming-v1.2+xml
Authorization: Bearer {{token}}
Content-Type: application/vnd.bose.streaming-v1.2+xml
User-Agent: Bose_Lisa/27.0.6
> {%
client.test("Response is 200 OK", function() {
client.assert(response.status === 200, "Response status is not 200");
});
client.test("Response body contains presets", function() {
const doc = response.body;
const presets = doc.getElementsByTagName("presets")[0];
client.assert(presets !== null, "Response body does not contain <presets>");
const presetList = doc.getElementsByTagName("preset");
// Based on the flow: set_preset_6, get_presets, delete_preset_6, set_preset_5
// The service is fresh, so only what we set is there.
client.assert(presetList.length === 1, "Response body should contain exactly one <preset>, but found " + presetList.length);
const firstPreset = presetList[0];
client.assert(firstPreset.getAttribute("buttonNumber") === "6", "First preset should have buttonNumber=\"6\"");
// Check <name>
const name = firstPreset.getElementsByTagName("name")[0];
client.assert(name.textContent === "SMOOTH JAZZ", "Preset name should be 'SMOOTH JAZZ'");
// Check <location>
const location = firstPreset.getElementsByTagName("location")[0];
client.assert(location.textContent === "/v1/playback/station/s166521", "Preset location mismatch");
// Check <source> and its attributes/children
const source = firstPreset.getElementsByTagName("source")[0];
client.assert(source !== null, "Preset should have a <source>");
// The source ID is set in HandleMargeUpdatePreset based on matching source, or in UpdatePreset.
// For TUNEIN it might be 10004 or similar in our mocks.
client.assert(source.getAttribute("id") !== null && source.getAttribute("id") !== "", "Source id should be non-empty");
client.assert(source.getAttribute("type") === "Audio", "Source type should be 'Audio'");
const sourceproviderid = source.getElementsByTagName("sourceproviderid")[0];
client.assert(sourceproviderid.textContent === "25", "sourceproviderid should be '25'");
// Check <username> inside <preset> (not the one in <source> if present)
const presetUsernames = firstPreset.getElementsByTagName("username");
// In the reference, there's one in <source> (empty) and one in <preset> (SMOOTH JAZZ)
// Usually, getElementsByTagName on firstPreset returns all descendants.
// Let's be careful about the index or use a more specific selector if possible,
// but here we can check the values.
let foundPresetUsername = false;
for (let i = 0; i < presetUsernames.length; i++) {
if (presetUsernames[i].textContent === "SMOOTH JAZZ") {
foundPresetUsername = true;
break;
}
}
client.assert(foundPresetUsername, "Preset should have <username>SMOOTH JAZZ</username>");
// Ensure <containerArt> is non-empty
const containerArt = firstPreset.getElementsByTagName("containerArt")[0];
client.assert(containerArt.textContent.startsWith("https://"), "containerArt should be a valid URL");
});
%}
@@ -0,0 +1,57 @@
### GET /streaming/account/{{accountId}}/device/{{deviceId}}/recents
GET {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}/recents
User-Agent: Bose_Lisa/27.0.6
Accept: application/vnd.bose.streaming-v1.2+xml
Authorization: Bearer {{token}}
> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
client.test("Response content-type is correct", function() {
var type = response.contentType.mimeType;
client.assert(type === "application/vnd.bose.streaming-v1.2+xml", "Expected 'application/vnd.bose.streaming-v1.2+xml' but received '" + type + "'");
});
client.test("Response body structure", function() {
var recents = response.body.getElementsByTagName("recents")[0];
client.assert(recents !== null, "Missing 'recents' root element");
var recentList = recents.getElementsByTagName("recent");
client.assert(recentList.length > 0, "No 'recent' elements found");
var recent = recentList[0];
client.assert(recent.getAttribute("id").length > 0, "Missing or empty 'id' attribute on 'recent' element");
client.assert(recent.getElementsByTagName("contentItemType")[0].textContent.length > 0, "Missing or empty 'contentItemType'");
client.assert(recent.getElementsByTagName("createdOn")[0].textContent.length > 0, "Missing or empty 'createdOn'");
client.assert(recent.getElementsByTagName("lastplayedat")[0].textContent.length > 0, "Missing or empty 'lastplayedat'");
client.assert(recent.getElementsByTagName("location")[0].textContent.length > 0, "Missing or empty 'location'");
client.assert(recent.getElementsByTagName("name")[0].textContent.length > 0, "Missing or empty 'name'");
var source = recent.getElementsByTagName("source")[0];
client.assert(source !== null, "Missing 'source' element");
client.assert(source.getAttribute("id").length > 0, "Missing or empty 'source' id");
client.assert(source.getAttribute("type").length > 0, "Missing or empty 'source' type");
client.assert(source.getElementsByTagName("createdOn")[0].textContent.length > 0, "Missing or empty source 'createdOn'");
var credential = source.getElementsByTagName("credential")[0];
client.assert(credential.getAttribute("type").length > 0, "Missing or empty credential 'type'");
client.assert(credential.textContent.length > 0, "Missing or empty credential value");
client.assert(source.getElementsByTagName("name")[0] !== null, "Missing source 'name'");
client.assert(source.getElementsByTagName("sourceproviderid")[0].textContent.length > 0, "Missing or empty source 'sourceproviderid'");
client.assert(source.getElementsByTagName("sourcename")[0].textContent.length > 0, "Missing or empty source 'sourcename'");
var sourceSettings = source.getElementsByTagName("sourceSettings")[0];
client.assert(sourceSettings !== undefined, "Missing 'sourceSettings'");
client.assert(source.getElementsByTagName("updatedOn")[0].textContent.length > 0, "Missing or empty source 'updatedOn'");
client.assert(source.getElementsByTagName("username")[0].textContent.length > 0, "Missing or empty source 'username'");
client.assert(recent.getElementsByTagName("sourceid")[0].textContent.length > 0, "Missing or empty 'sourceid'");
client.assert(recent.getElementsByTagName("updatedOn")[0].textContent.length > 0, "Missing or empty 'updatedOn'");
});
%}
@@ -7,3 +7,13 @@ Authorization: Bearer {{token}}
client.assert(response.status === 200, "Response status is not 200");
});
%}
### DELETE /streaming/account/{{accountId}}/device/{{deviceId}} (Unregister Device Variant)
DELETE {{host}}/streaming/account/{{accountId}}/device/{{deviceId}}
Authorization: Bearer {{token}}
> {%
client.test("Device unregistered successfully (variant)", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}