diff --git a/pkg/manifest/a11y.go b/pkg/manifest/a11y.go index 69d00be..278e16a 100644 --- a/pkg/manifest/a11y.go +++ b/pkg/manifest/a11y.go @@ -17,7 +17,7 @@ type A11y struct { Certification *A11yCertification `json:"certification,omitempty"` // Certification of accessible publications. Summary string `json:"summary,omitempty"` // A human-readable summary of specific accessibility features or deficiencies, consistent with the other accessibility metadata but expressing subtleties such as "short descriptions are present but long descriptions will be needed for non-visual users" or "short descriptions are present and no long descriptions are needed." AccessModes []A11yAccessMode `json:"accessMode,omitempty"` // The human sensory perceptual system or cognitive faculty through which a person may process or perceive information. - AccessModesSufficient [][]A11yPrimaryAccessMode `json:"accessModeSufficient,omitempty"` // A list of single or combined accessModes that are sufficient to understand all the intellectual content of a resource. + AccessModesSufficient A11yPrimaryAccessModeList `json:"accessModeSufficient,omitempty"` // A list of single or combined accessModes that are sufficient to understand all the intellectual content of a resource. Features []A11yFeature `json:"feature,omitempty"` // Content features of the resource, such as accessible media, alternatives and supported enhancements for accessibility. Hazards []A11yHazard `json:"hazard,omitempty"` // A characteristic of the described resource that is physiologically dangerous to some users. Exemptions []A11yExemption `json:"exemption,omitempty"` // Justifications for non-conformance based on exemptions in a given jurisdiction. @@ -28,7 +28,7 @@ func NewA11y() A11y { return A11y{ ConformsTo: A11yProfileList{}, AccessModes: []A11yAccessMode{}, - AccessModesSufficient: [][]A11yPrimaryAccessMode{}, + AccessModesSufficient: A11yPrimaryAccessModeList{}, Features: []A11yFeature{}, Hazards: []A11yHazard{}, Exemptions: []A11yExemption{}, @@ -109,7 +109,7 @@ func A11yFromJSON(rawJSON map[string]interface{}) (*A11y, error) { } a.AccessModes = A11yAccessModesFromStrings(accessModes) - ams := [][]A11yPrimaryAccessMode{} + ams := A11yPrimaryAccessModeList{} if amsJSON, ok := rawJSON["accessModeSufficient"].([]interface{}); ok { for _, l := range amsJSON { strings, err := parseSliceOrString(l, true) @@ -229,6 +229,15 @@ func (p A11yProfile) Compare(other A11yProfile) int { type A11yProfileList []A11yProfile +func (l A11yProfileList) MarshalJSON() ([]byte, error) { + if len(l) == 1 { + return json.Marshal(l[0]) + } + + type alias A11yProfileList + return json.Marshal(alias(l)) +} + func (l A11yProfileList) Sort() { if len(l) <= 1 { return @@ -300,6 +309,17 @@ func A11yAccessModesFromStrings(strings []string) []A11yAccessMode { // A11yPrimaryAccessMode is a human primary sensory perceptual system or cognitive faculty through which a person may process or perceive information. type A11yPrimaryAccessMode string +type A11yPrimaryAccessModeList [][]A11yPrimaryAccessMode + +func (l A11yPrimaryAccessModeList) MarshalJSON() ([]byte, error) { + if len(l) == 1 && len(l[0]) == 1 { + return json.Marshal(l[0][0]) + } + + type alias A11yPrimaryAccessModeList + return json.Marshal(alias(l)) +} + const ( // Indicates that auditory perception is necessary to consume the information. A11yPrimaryAccessModeAuditory A11yPrimaryAccessMode = "auditory" diff --git a/pkg/manifest/a11y_test.go b/pkg/manifest/a11y_test.go index bf4fcea..1507a9c 100644 --- a/pkg/manifest/a11y_test.go +++ b/pkg/manifest/a11y_test.go @@ -45,7 +45,7 @@ func TestA11yUnmarshalFullJSON(t *testing.T) { A11yAccessModeAuditory, A11yAccessModeChartOnVisual, }, - AccessModesSufficient: [][]A11yPrimaryAccessMode{ + AccessModesSufficient: A11yPrimaryAccessModeList{ { A11yPrimaryAccessModeVisual, A11yPrimaryAccessModeTactile, @@ -110,7 +110,7 @@ func TestA11yUnmarshalAccessModeSufficientContainingBothStringsAndArrays(t *test var m A11y require.NoError(t, json.Unmarshal([]byte(`{"accessModeSufficient": ["auditory", ["visual", "tactile"], [], "visual"]}`), &m)) var e A11y = NewA11y() - e.AccessModesSufficient = [][]A11yPrimaryAccessMode{ + e.AccessModesSufficient = A11yPrimaryAccessModeList{ {A11yPrimaryAccessModeAuditory}, {A11yPrimaryAccessModeVisual, A11yPrimaryAccessModeTactile}, {A11yPrimaryAccessModeVisual}, @@ -122,7 +122,7 @@ func TestA11yMarshalMinimalJSON(t *testing.T) { m := A11y{ ConformsTo: []A11yProfile{}, AccessModes: []A11yAccessMode{}, - AccessModesSufficient: [][]A11yPrimaryAccessMode{}, + AccessModesSufficient: A11yPrimaryAccessModeList{}, Features: []A11yFeature{}, Hazards: []A11yHazard{}, Exemptions: []A11yExemption{}, @@ -148,7 +148,7 @@ func TestA11yMarshalFullJSON(t *testing.T) { A11yAccessModeAuditory, A11yAccessModeChartOnVisual, }, - AccessModesSufficient: [][]A11yPrimaryAccessMode{ + AccessModesSufficient: A11yPrimaryAccessModeList{ {A11yPrimaryAccessModeAuditory}, { A11yPrimaryAccessModeVisual, @@ -177,3 +177,33 @@ func TestA11yMarshalFullJSON(t *testing.T) { []byte(`{"conformsTo":["http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-a","https://profile2"],"certification":{"certifiedBy":"company1","credential":"credential1","report":"https://report1"},"summary":"Summary","accessMode":["auditory","chartOnVisual"],"accessModeSufficient":[["auditory"],["visual","tactile"],["visual"]],"feature":["readingOrder","alternativeText"],"hazard":["flashing","motionSimulation"],"exemption":["eaa-fundamental-alteration","eaa-microenterprise"]}`), ) } + +func TestA11yMarshalConformsToSingleValue(t *testing.T) { + m := A11y{ + ConformsTo: []A11yProfile{ + EPUBA11y11WCAG22AA, + }, + } + data, err := json.Marshal(m) + require.NoError(t, err) + assert.Equal( + t, + []byte(`{"conformsTo":"https://www.w3.org/TR/epub-a11y-11#wcag-2.2-aa"}`), + data, + ) +} + +func TestA11yMarshalAccessModeSufficientSingleValue(t *testing.T) { + m := A11y{ + AccessModesSufficient: A11yPrimaryAccessModeList{ + {A11yPrimaryAccessModeTextual}, + }, + } + data, err := json.Marshal(m) + require.NoError(t, err) + assert.Equal( + t, + []byte(`{"accessModeSufficient":"textual"}`), + data, + ) +} diff --git a/pkg/manifest/metadata_test.go b/pkg/manifest/metadata_test.go index 9d5525b..47c4e8f 100644 --- a/pkg/manifest/metadata_test.go +++ b/pkg/manifest/metadata_test.go @@ -297,7 +297,7 @@ func TestMetadataFullJSON(t *testing.T) { ], "title": {"en": "Title", "fr": "Titre"}, "subtitle": {"en": "Subtitle", "fr": "Sous-titre"}, - "accessibility": {"conformsTo": ["http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-aa"]}, + "accessibility": {"conformsTo": "http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-aa"}, "tdm": {"policy": "https://provider.com/policies/policy.json", "reservation": "all"}, "modified": "2001-01-01T12:36:27.123Z", "published": "2001-01-02T12:36:27Z", diff --git a/pkg/parser/epub/metadata.go b/pkg/parser/epub/metadata.go index d2590fe..2960ae8 100644 --- a/pkg/parser/epub/metadata.go +++ b/pkg/parser/epub/metadata.go @@ -819,8 +819,8 @@ func valuesToAccessModes(values []string) []manifest.A11yAccessMode { return am } -func valuesToAccessModesSufficient(values []string) [][]manifest.A11yPrimaryAccessMode { - ams := make([][]manifest.A11yPrimaryAccessMode, 0, len(values)) +func valuesToAccessModesSufficient(values []string) manifest.A11yPrimaryAccessModeList { + ams := make(manifest.A11yPrimaryAccessModeList, 0, len(values)) for _, v := range values { c := a11yAccessModesSufficient(v) if len(c) > 0 { diff --git a/pkg/parser/epub/metadata_test.go b/pkg/parser/epub/metadata_test.go index fefc650..c510f93 100644 --- a/pkg/parser/epub/metadata_test.go +++ b/pkg/parser/epub/metadata_test.go @@ -319,7 +319,7 @@ func TestMetadataEPUB2Accessibility(t *testing.T) { } e.Summary = "The publication contains structural and page navigation." e.AccessModes = []manifest.A11yAccessMode{manifest.A11yAccessModeTextual, manifest.A11yAccessModeVisual} - e.AccessModesSufficient = [][]manifest.A11yPrimaryAccessMode{ + e.AccessModesSufficient = manifest.A11yPrimaryAccessModeList{ {manifest.A11yPrimaryAccessModeTextual}, {manifest.A11yPrimaryAccessModeTextual, manifest.A11yPrimaryAccessModeVisual}, } @@ -351,7 +351,7 @@ func TestMetadataEPUB3Accessibility(t *testing.T) { } e.Summary = "The publication contains structural and page navigation." e.AccessModes = []manifest.A11yAccessMode{manifest.A11yAccessModeTextual, manifest.A11yAccessModeVisual} - e.AccessModesSufficient = [][]manifest.A11yPrimaryAccessMode{ + e.AccessModesSufficient = manifest.A11yPrimaryAccessModeList{ {manifest.A11yPrimaryAccessModeTextual}, {manifest.A11yPrimaryAccessModeTextual, manifest.A11yPrimaryAccessModeVisual}, } @@ -372,7 +372,7 @@ func TestMetadataEPUB3AccessibilityRefines(t *testing.T) { CertifiedBy: "Standard Ebooks", } e.AccessModes = manifest.A11yAccessModesFromStrings([]string{"textual"}) - e.AccessModesSufficient = [][]manifest.A11yPrimaryAccessMode{ + e.AccessModesSufficient = manifest.A11yPrimaryAccessModeList{ a11yAccessModesSufficient("textual"), } e.Features = valuesToA11yFeatures([]string{"readingOrder", "structuralNavigation", "tableOfContents", "unlocked"}) diff --git a/pkg/streamer/a11y_infer_test.go b/pkg/streamer/a11y_infer_test.go index 177c4c3..f969f2a 100644 --- a/pkg/streamer/a11y_infer_test.go +++ b/pkg/streamer/a11y_infer_test.go @@ -29,7 +29,7 @@ func TestReturnsAdditionalInferredA11yMetadata(t *testing.T) { inferreddA11y := manifest.NewA11y() inferreddA11y.AccessModes = []manifest.A11yAccessMode{manifest.A11yAccessModeTextual} - inferreddA11y.AccessModesSufficient = [][]manifest.A11yPrimaryAccessMode{{manifest.A11yPrimaryAccessModeTextual}} + inferreddA11y.AccessModesSufficient = manifest.A11yPrimaryAccessModeList{{manifest.A11yPrimaryAccessModeTextual}} res, err := inferA11yMetadataInPublicationManifest(context.TODO(), pub.New(m, nil, nil), nil) require.NoError(t, err)