mirror of
https://github.com/photoprism/photoprism.git
synced 2026-07-17 16:49:04 +00:00
* Tests: Stabilize sidebar-edit-003 and add HEIF JPEG fallback coverage * Tests: Stabilize lightbox sidebar-edit suite on fast systems
This commit is contained in:
parent
aa4d51bed1
commit
4005aea4fe
7 changed files with 246 additions and 79 deletions
|
|
@ -47,9 +47,8 @@ test.meta("testID", "sidebar-edit-001").meta({ mode: "public" })(
|
|||
await t.expect(photoviewer.sidebarRow(field.icon).withText(field.value).exists).ok();
|
||||
}
|
||||
|
||||
// The backend normalizes keywords (split on whitespace/commas,
|
||||
// lowercased, deduped), so we assert on a unique single-word token
|
||||
// that survives that pass.
|
||||
// The backend lower-cases and dedupes keywords; assert on a single
|
||||
// lowercase token that survives that pass.
|
||||
const keywordsInput = await photoviewer.startInlineEditBySection("Keywords");
|
||||
await t.typeText(keywordsInput, "sidebareditkw", { replace: true });
|
||||
await photoviewer.confirmInlineEditBySection("Keywords");
|
||||
|
|
@ -90,77 +89,140 @@ test.meta("testID", "sidebar-edit-003").meta({ mode: "public" })(
|
|||
async (t) => {
|
||||
await photoviewer.openSidebarOnFirstPhoto();
|
||||
|
||||
// Date / time dialog: change all five user-editable fields. Re-opening
|
||||
// the dialog after save is the most direct check that each field round-
|
||||
// tripped through the API; the sidebar only formats a subset of these
|
||||
// (year is always rendered, timezone only when set).
|
||||
const dateTimeDialog = photoviewer.dateTimeDialog;
|
||||
const cameraDialog = photoviewer.cameraDialog;
|
||||
const locationDialog = photoviewer.locationDialog;
|
||||
const optionWith = (text) => Selector('div[role="option"]').withText(text);
|
||||
|
||||
// Clicking a rendered option is racy on fast systems where the list
|
||||
// re-mounts between visibility and click; keyboard nav is stable.
|
||||
const pickAutocomplete = async (input, value) => {
|
||||
await t.typeText(input, value, { replace: true }).pressKey("down enter");
|
||||
};
|
||||
const pickFromSelect = async (field, value) => {
|
||||
await t.click(field).click(optionWith(value));
|
||||
};
|
||||
|
||||
// Other tests in this suite edit the same photo; snapshot now and
|
||||
// restore at the end so leftover timezone/values don't leak.
|
||||
await photoviewer.openSidebarDialog("takenAt");
|
||||
const yearInput = photoviewer.dateTimeDialog.find(".input-year input");
|
||||
const monthInput = photoviewer.dateTimeDialog.find(".input-month input");
|
||||
const dayInput = photoviewer.dateTimeDialog.find(".input-day input");
|
||||
const timeInput = photoviewer.dateTimeDialog.find(".input-local-time input");
|
||||
const timeZoneInput = photoviewer.dateTimeDialog.find(".input-timezone input");
|
||||
await t.typeText(yearInput, "2022", { replace: true }).pressKey("tab");
|
||||
await t.typeText(monthInput, "7", { replace: true }).pressKey("tab");
|
||||
await t.typeText(dayInput, "15", { replace: true }).pressKey("tab");
|
||||
await t.typeText(timeInput, "13:45:30", { replace: true }).pressKey("tab");
|
||||
await t.typeText(timeZoneInput, "UTC", { replace: true }).pressKey("enter");
|
||||
await t.click(photoviewer.dateTimeDialog.find(".action-confirm"));
|
||||
await t.expect(photoviewer.dateTimeDialog.visible).notOk();
|
||||
await t.expect(photoviewer.sidebarRow("mdi-calendar").withText("2022").exists).ok();
|
||||
const initialYear = await dateTimeDialog.yearValue.innerText;
|
||||
const initialMonth = await dateTimeDialog.monthValue.innerText;
|
||||
const initialDay = await dateTimeDialog.dayValue.innerText;
|
||||
const initialLocalTime = await dateTimeDialog.localTime.value;
|
||||
const initialTimezone = await dateTimeDialog.timezoneValue.innerText;
|
||||
await t.click(dateTimeDialog.cancel);
|
||||
await t.expect(dateTimeDialog.root.visible).notOk();
|
||||
|
||||
// Re-open and verify each input was persisted. The autocompletes render
|
||||
// their selected value as the input's `value` attribute, so we read it
|
||||
// back rather than relying on locale-formatted display strings.
|
||||
await photoviewer.openSidebarDialog("takenAt");
|
||||
await t.expect(yearInput.value).eql("2022");
|
||||
await t.expect(monthInput.value).eql("7");
|
||||
await t.expect(dayInput.value).eql("15");
|
||||
await t.expect(timeInput.value).eql("13:45:30");
|
||||
await t.expect(timeZoneInput.value).eql("UTC");
|
||||
await t.click(photoviewer.dateTimeDialog.find(".action-cancel"));
|
||||
await t.expect(photoviewer.dateTimeDialog.visible).notOk();
|
||||
|
||||
// Camera dialog: change every numeric field. Camera/Lens autocompletes
|
||||
// are intentionally skipped — their item lists depend on backend fixture
|
||||
// data, which varies across acceptance environments. The four numeric
|
||||
// fields cover the dialog's persistence path end-to-end.
|
||||
await photoviewer.openSidebarDialog("camera");
|
||||
const isoInput = photoviewer.cameraDialog.find(".input-iso input");
|
||||
const exposureInput = photoviewer.cameraDialog.find(".input-exposure input");
|
||||
const fNumberInput = photoviewer.cameraDialog.find(".input-fnumber input");
|
||||
const focalLengthInput = photoviewer.cameraDialog.find(".input-focal-length input");
|
||||
await t.typeText(isoInput, "6400", { replace: true });
|
||||
await t.typeText(exposureInput, "1/250", { replace: true });
|
||||
await t.typeText(fNumberInput, "1.8", { replace: true });
|
||||
await t.typeText(focalLengthInput, "35", { replace: true });
|
||||
await t.click(photoviewer.cameraDialog.find(".action-confirm"));
|
||||
await t.expect(photoviewer.cameraDialog.visible).notOk();
|
||||
const initialCamera = await cameraDialog.cameraValue.innerText;
|
||||
const initialLens = await cameraDialog.lensValue.innerText;
|
||||
const initialIso = await cameraDialog.iso.value;
|
||||
const initialExposure = await cameraDialog.exposure.value;
|
||||
const initialFnumber = await cameraDialog.fnumber.value;
|
||||
const initialFocalLength = await cameraDialog.focalLength.value;
|
||||
await t.click(cameraDialog.cancel);
|
||||
await t.expect(cameraDialog.root.visible).notOk();
|
||||
|
||||
// Re-open and verify every numeric field came back from the backend.
|
||||
await photoviewer.openSidebarDialog("camera");
|
||||
await t.expect(isoInput.value).eql("6400");
|
||||
await t.expect(exposureInput.value).eql("1/250");
|
||||
await t.expect(fNumberInput.value).eql("1.8");
|
||||
await t.expect(focalLengthInput.value).eql("35");
|
||||
await t.click(photoviewer.cameraDialog.find(".action-cancel"));
|
||||
await t.expect(photoviewer.cameraDialog.visible).notOk();
|
||||
|
||||
// Location dialog: a raw coordinate string avoids hitting an external
|
||||
// reverse-geocoder, which keeps the test deterministic in offline
|
||||
// acceptance environments.
|
||||
await photoviewer.openSidebarDialog("location");
|
||||
const coordsInput = photoviewer.locationDialog.find(".input-coordinates input");
|
||||
await t.expect(coordsInput.visible).ok();
|
||||
await t.typeText(coordsInput, "52.5200, 13.4050", { replace: true }).pressKey("enter");
|
||||
await t.click(photoviewer.locationDialog.find(".action-confirm"));
|
||||
await t.expect(photoviewer.locationDialog.visible).notOk();
|
||||
const initialCoordinates = await locationDialog.coordinates.value;
|
||||
await t.click(locationDialog.cancel);
|
||||
await t.expect(locationDialog.root.visible).notOk();
|
||||
|
||||
await photoviewer.openSidebarDialog("takenAt");
|
||||
await pickAutocomplete(dateTimeDialog.year, "2022");
|
||||
await pickAutocomplete(dateTimeDialog.month, "07");
|
||||
await pickAutocomplete(dateTimeDialog.day, "15");
|
||||
await t.typeText(dateTimeDialog.localTime, "13:45:30", { replace: true }).pressKey("tab");
|
||||
await pickAutocomplete(dateTimeDialog.timezone, "UTC");
|
||||
await t.click(dateTimeDialog.confirm);
|
||||
await t.expect(dateTimeDialog.root.visible).notOk();
|
||||
|
||||
// For UTC photos formatTime() drops the zone abbreviation, so "UTC"
|
||||
// never appears in the sidebar text — verified via the dialog below.
|
||||
const calendarRow = photoviewer.sidebarRow("mdi-calendar");
|
||||
await t.expect(calendarRow.withText("2022").exists).ok();
|
||||
await t.expect(calendarRow.withText("Jul").exists).ok();
|
||||
await t.expect(calendarRow.withText("15").exists).ok();
|
||||
|
||||
await photoviewer.openSidebarDialog("takenAt");
|
||||
await t.expect(dateTimeDialog.yearValue.innerText).eql("2022");
|
||||
await t.expect(dateTimeDialog.monthValue.innerText).eql("07");
|
||||
await t.expect(dateTimeDialog.dayValue.innerText).eql("15");
|
||||
await t.expect(dateTimeDialog.localTime.value).eql("13:45:30");
|
||||
await t.expect(dateTimeDialog.timezoneValue.innerText).eql("UTC");
|
||||
await t.click(dateTimeDialog.cancel);
|
||||
await t.expect(dateTimeDialog.root.visible).notOk();
|
||||
|
||||
const cameraName = "Canon EOS M10";
|
||||
const lensName = "EF-M15-45mm f/3.5-6.3 IS STM";
|
||||
await photoviewer.openSidebarDialog("camera");
|
||||
await pickFromSelect(cameraDialog.camera, cameraName);
|
||||
await pickFromSelect(cameraDialog.lens, lensName);
|
||||
await t.typeText(cameraDialog.iso, "6400", { replace: true });
|
||||
await t.typeText(cameraDialog.exposure, "1/250", { replace: true });
|
||||
await t.typeText(cameraDialog.fnumber, "1.8", { replace: true });
|
||||
await t.typeText(cameraDialog.focalLength, "35", { replace: true });
|
||||
await t.click(cameraDialog.confirm);
|
||||
await t.expect(cameraDialog.root.visible).notOk();
|
||||
|
||||
const cameraRow = photoviewer.sidebarRow("mdi-camera");
|
||||
await t.expect(cameraRow.withText(cameraName).exists).ok();
|
||||
await t.expect(cameraRow.withText("ISO 6400").exists).ok();
|
||||
await t.expect(cameraRow.withText("1/250").exists).ok();
|
||||
|
||||
await photoviewer.openSidebarDialog("camera");
|
||||
await t.expect(cameraDialog.cameraValue.innerText).eql(cameraName);
|
||||
await t.expect(cameraDialog.lensValue.innerText).eql(lensName);
|
||||
await t.expect(cameraDialog.iso.value).eql("6400");
|
||||
await t.expect(cameraDialog.exposure.value).eql("1/250");
|
||||
await t.expect(cameraDialog.fnumber.value).eql("1.8");
|
||||
await t.expect(cameraDialog.focalLength.value).eql("35");
|
||||
await t.click(cameraDialog.cancel);
|
||||
await t.expect(cameraDialog.root.visible).notOk();
|
||||
|
||||
// Raw coordinates avoid hitting the external reverse-geocoder.
|
||||
await photoviewer.openSidebarDialog("location");
|
||||
await t.expect(locationDialog.coordinates.visible).ok();
|
||||
await t.typeText(locationDialog.coordinates, "52.5200, 13.4050", { replace: true }).pressKey("enter");
|
||||
await t.click(locationDialog.confirm);
|
||||
await t.expect(locationDialog.root.visible).notOk();
|
||||
await t.expect(Selector(".p-sidebar-info .p-map").exists).ok();
|
||||
|
||||
// The coordinates row title is composed by Photo.getLatLng(); we assert
|
||||
// both halves so the test fails noisily if either axis fails to persist.
|
||||
// Substring match — the optional " · <altitude> m" suffix can follow.
|
||||
const coordinatesRow = photoviewer.sidebarRow("mdi-map-marker").nextSibling(".v-list-item");
|
||||
await t.expect(coordinatesRow.getAttribute("title")).contains("52.52");
|
||||
await t.expect(coordinatesRow.getAttribute("title")).contains("13.405");
|
||||
await t.expect(coordinatesRow.withText("52.52000").exists).ok();
|
||||
await t.expect(coordinatesRow.withText("13.40500").exists).ok();
|
||||
|
||||
// Skip empty fields: typeText("") is a no-op and v-select has no clear.
|
||||
await photoviewer.openSidebarDialog("takenAt");
|
||||
if (initialYear) await pickAutocomplete(dateTimeDialog.year, initialYear);
|
||||
if (initialMonth) await pickAutocomplete(dateTimeDialog.month, initialMonth);
|
||||
if (initialDay) await pickAutocomplete(dateTimeDialog.day, initialDay);
|
||||
if (initialLocalTime) {
|
||||
await t.typeText(dateTimeDialog.localTime, initialLocalTime, { replace: true }).pressKey("tab");
|
||||
}
|
||||
if (initialTimezone) await pickAutocomplete(dateTimeDialog.timezone, initialTimezone);
|
||||
await t.click(dateTimeDialog.confirm);
|
||||
await t.expect(dateTimeDialog.root.visible).notOk();
|
||||
|
||||
await photoviewer.openSidebarDialog("camera");
|
||||
if (initialCamera) await pickFromSelect(cameraDialog.camera, initialCamera);
|
||||
if (initialLens) await pickFromSelect(cameraDialog.lens, initialLens);
|
||||
if (initialIso) await t.typeText(cameraDialog.iso, initialIso, { replace: true });
|
||||
if (initialExposure) await t.typeText(cameraDialog.exposure, initialExposure, { replace: true });
|
||||
if (initialFnumber) await t.typeText(cameraDialog.fnumber, initialFnumber, { replace: true });
|
||||
if (initialFocalLength) {
|
||||
await t.typeText(cameraDialog.focalLength, initialFocalLength, { replace: true });
|
||||
}
|
||||
await t.click(cameraDialog.confirm);
|
||||
await t.expect(cameraDialog.root.visible).notOk();
|
||||
|
||||
if (initialCoordinates) {
|
||||
await photoviewer.openSidebarDialog("location");
|
||||
await t.typeText(locationDialog.coordinates, initialCoordinates, { replace: true }).pressKey("enter");
|
||||
await t.click(locationDialog.confirm);
|
||||
await t.expect(locationDialog.root.visible).notOk();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
18
frontend/tests/acceptance/page-model/dialog/camera.js
Normal file
18
frontend/tests/acceptance/page-model/dialog/camera.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Selector } from "testcafe";
|
||||
|
||||
export default class CameraDialog {
|
||||
constructor() {
|
||||
this.root = Selector(".p-camera-dialog", { timeout: 15000 });
|
||||
this.camera = this.root.find(".input-camera");
|
||||
this.lens = this.root.find(".input-lens");
|
||||
// v-select renders the chosen option in `.v-select__selection-text`.
|
||||
this.cameraValue = this.root.find(".input-camera .v-select__selection-text");
|
||||
this.lensValue = this.root.find(".input-lens .v-select__selection-text");
|
||||
this.iso = this.root.find(".input-iso input");
|
||||
this.exposure = this.root.find(".input-exposure input");
|
||||
this.fnumber = this.root.find(".input-fnumber input");
|
||||
this.focalLength = this.root.find(".input-focal-length input");
|
||||
this.confirm = this.root.find(".action-confirm");
|
||||
this.cancel = this.root.find(".action-cancel");
|
||||
}
|
||||
}
|
||||
20
frontend/tests/acceptance/page-model/dialog/date-time.js
Normal file
20
frontend/tests/acceptance/page-model/dialog/date-time.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { Selector } from "testcafe";
|
||||
|
||||
export default class DateTimeDialog {
|
||||
constructor() {
|
||||
this.root = Selector(".p-datetime-dialog", { timeout: 15000 });
|
||||
this.year = this.root.find(".input-year input");
|
||||
this.month = this.root.find(".input-month input");
|
||||
this.day = this.root.find(".input-day input");
|
||||
this.localTime = this.root.find(".input-local-time input");
|
||||
this.timezone = this.root.find(".input-timezone input");
|
||||
// v-autocomplete renders the chosen option text in a sibling element;
|
||||
// the inner <input>.value tracks the search/filter string instead.
|
||||
this.yearValue = this.root.find(".input-year .v-autocomplete__selection");
|
||||
this.monthValue = this.root.find(".input-month .v-autocomplete__selection");
|
||||
this.dayValue = this.root.find(".input-day .v-autocomplete__selection");
|
||||
this.timezoneValue = this.root.find(".input-timezone .v-autocomplete__selection");
|
||||
this.confirm = this.root.find(".action-confirm");
|
||||
this.cancel = this.root.find(".action-cancel");
|
||||
}
|
||||
}
|
||||
10
frontend/tests/acceptance/page-model/dialog/location.js
Normal file
10
frontend/tests/acceptance/page-model/dialog/location.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Selector } from "testcafe";
|
||||
|
||||
export default class LocationDialog {
|
||||
constructor() {
|
||||
this.root = Selector(".p-location-dialog", { timeout: 15000 });
|
||||
this.coordinates = this.root.find(".input-coordinates input");
|
||||
this.confirm = this.root.find(".action-confirm");
|
||||
this.cancel = this.root.find(".action-cancel");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
import { Selector, t } from "testcafe";
|
||||
import Toolbar from "./toolbar";
|
||||
import Photo from "./photo";
|
||||
import DateTimeDialog from "./dialog/date-time";
|
||||
import CameraDialog from "./dialog/camera";
|
||||
import LocationDialog from "./dialog/location";
|
||||
|
||||
export default class Page {
|
||||
constructor() {
|
||||
|
|
@ -45,11 +48,10 @@ export default class Page {
|
|||
this.sidebarChips = Selector(".p-sidebar-info .meta-chip", { timeout: 15000 });
|
||||
this.faceMarkerEjectButton = Selector(".metadata__person-row .meta-marker-eject", { timeout: 15000 });
|
||||
this.faceMarkerNameInput = Selector(".metadata__person-row .meta-inline-marker input", { timeout: 15000 });
|
||||
// Edit dialogs launched from the sidebar pencils. Timeouts are generous
|
||||
// enough for Vuetify teleport mounting + reverse-geocoder lookups.
|
||||
this.dateTimeDialog = Selector(".p-datetime-dialog", { timeout: 15000 });
|
||||
this.cameraDialog = Selector(".p-camera-dialog", { timeout: 15000 });
|
||||
this.locationDialog = Selector(".p-location-dialog", { timeout: 15000 });
|
||||
// Edit dialogs launched from the sidebar pencils.
|
||||
this.dateTimeDialog = new DateTimeDialog();
|
||||
this.cameraDialog = new CameraDialog();
|
||||
this.locationDialog = new LocationDialog();
|
||||
}
|
||||
|
||||
// Locate the v-list-item that contains a given MDI prepend-icon.
|
||||
|
|
@ -103,6 +105,10 @@ export default class Page {
|
|||
await t.wait(200);
|
||||
await t.pressKey("enter");
|
||||
await this.confirmInlineEditBySection(sectionLabel);
|
||||
// The combobox menu teleports to <body>; on fast systems a lingering
|
||||
// overlay intercepts the next click (notably the lightbox close).
|
||||
await t.expect(Selector(".p-sidebar-info .meta-inline-edit:not(.meta-inline-marker)").exists).notOk();
|
||||
await t.expect(Selector(".meta-inline-menu").exists).notOk();
|
||||
}
|
||||
|
||||
// Title/Caption enter editing from either a pencil (value present) or
|
||||
|
|
@ -129,16 +135,15 @@ export default class Page {
|
|||
async openSidebarDialog(which) {
|
||||
if (which === "takenAt") {
|
||||
await t.click(this.sidebarRow("mdi-calendar").find(".meta-inline-pencil"));
|
||||
await t.expect(this.dateTimeDialog.visible).ok();
|
||||
await t.expect(this.dateTimeDialog.root.visible).ok();
|
||||
} else if (which === "camera") {
|
||||
await t.click(this.sidebarRow("mdi-camera").find(".meta-inline-pencil"));
|
||||
await t.expect(this.cameraDialog.visible).ok();
|
||||
await t.expect(this.cameraDialog.root.visible).ok();
|
||||
} else if (which === "location") {
|
||||
// Two rows (Location and Coordinates) host a location pencil depending
|
||||
// on whether the photo has lat/lng; both carry the modifier class that
|
||||
// disambiguates them from the other inline metadata pencils.
|
||||
// Both the Location and Coordinates rows expose a pencil with this
|
||||
// modifier class; either one opens the same dialog.
|
||||
await t.click(Selector(".p-sidebar-info .meta-inline-pencil--location"));
|
||||
await t.expect(this.locationDialog.visible).ok();
|
||||
await t.expect(this.locationDialog.root.visible).ok();
|
||||
} else {
|
||||
throw new Error(`Unknown sidebar dialog: ${which}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ type SearchPhotosGeo struct {
|
|||
People string `form:"people" example:"people:\"Jane & John\"" notes:"Subject names, combinable with & or |"`
|
||||
Subjects string `form:"subjects" example:"subjects:\"Jane & John\"" notes:"Alias for people"` // Alias for People
|
||||
Keywords string `form:"keywords" example:"keywords:\"sand&water\"" notes:"Keywords, combinable with & or |"`
|
||||
Label string `form:"label" example:"label:cat|dog" notes:"Label names, separated by |"`
|
||||
Label string `form:"label" example:"label:\"cat|dog&!blurry\"" notes:"Label names: | is OR within a group, & is AND between groups (every positive group must match), leading ! negates a group (e.g. !rejected). Category expansion applies to both positive and negative terms. Escape a literal &, |, or leading ! with \\"`
|
||||
Category string `form:"category" example:"category:airport" notes:"Location category type"`
|
||||
Album string `form:"album" example:"album:berlin" notes:"Album UID or name, supports * wildcards"`
|
||||
Albums string `form:"albums" example:"albums:\"South Africa & Birds\"" notes:"Album names, combinable with & or |"`
|
||||
|
|
|
|||
|
|
@ -283,6 +283,58 @@ func TestConvert_JpegConvertCmds(t *testing.T) {
|
|||
assert.True(t, found)
|
||||
}
|
||||
|
||||
// TestConvert_JpegConvertCmds_HeifFallback verifies that the documented external
|
||||
// fallback command is emitted for HEIC and AVIF inputs when libheif tooling
|
||||
// (heif-dec / heif-convert) is available — see issue #5509.
|
||||
func TestConvert_JpegConvertCmds_HeifFallback(t *testing.T) {
|
||||
cnf := config.TestConfig()
|
||||
|
||||
if !cnf.HeifConvertEnabled() {
|
||||
t.Skip("heif-dec/heif-convert must be available for the HEIF fallback path")
|
||||
}
|
||||
|
||||
convert := NewConvert(cnf)
|
||||
heifBin := filepath.Base(cnf.HeifConvertBin())
|
||||
|
||||
cases := []struct {
|
||||
name, src, dst string
|
||||
}{
|
||||
{"Heic", "iphone_15_pro.heic", "iphone_15_pro.heic.jpg"},
|
||||
{"Avif", "fox.profile0.8bpc.yuv420.avif", "fox.profile0.8bpc.yuv420.avif.jpg"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
srcFile := filepath.Join(cnf.SamplesPath(), tc.src)
|
||||
dstFile := filepath.Join(cnf.SidecarPath(), cnf.SamplesPath(), tc.dst)
|
||||
|
||||
mediaFile, err := NewMediaFile(srcFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmds, useMutex, err := convert.JpegConvertCmds(mediaFile, dstFile, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, useMutex)
|
||||
assert.NotEmpty(t, cmds)
|
||||
|
||||
found := false
|
||||
for _, cmd := range cmds {
|
||||
s := cmd.String()
|
||||
if strings.Contains(s, heifBin) && strings.Contains(s, srcFile) && strings.Contains(s, dstFile) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assert.True(t, found, "expected a %s fallback command for %s", heifBin, tc.src)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvert_PngConvertCmds(t *testing.T) {
|
||||
cnf := config.TestConfig()
|
||||
convert := NewConvert(cnf)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue