/** * @copyright Copyright (c) Open-Xchange GmbH, Germany <info@open-xchange.com> * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with OX App Suite. If not, see <https://www.gnu.org/licenses/agpl-3.0.txt>. * * Any use of the work other than as authorized under this license or copyright law is prohibited. */ import { expect } from 'chai' import * as td from 'testdouble' import { injectApp, mockConfig, mockFetch, mockRedis } from './util.js' describe('Service delivers a generated web-manifest', function () { let app before(async function () { await mockConfig({ urls: ['http://ui-server/'] }) await mockRedis() app = await injectApp() }) after(async function () { td.reset() }) afterEach(async function () { await import('../src/redis.js').then(({ client }) => client.flushdb()) await import('../src/cache.js').then(({ clear }) => clear()) }) it('delivers valid webmanifest with short syntax', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Valid App Suite', pwa: { enabled: true, name: 'Valid App Suite', shortName: 'Valid App Suite', icon: '/mycustomlogo.png', iconWidthHeight: 512, backgroundColor: 'white' } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(200) expect(response.headers['content-type']).to.equal('application/manifest+json; charset=utf-8') expect(response.json()).to.deep.include({ name: 'Valid App Suite', short_name: 'Valid App Suite', icons: [{ src: '/mycustomlogo.png', type: 'image/png', sizes: '512x512', purpose: 'any' }], background_color: 'white' }) }) it('delivers no manifest with pwa.enabled=false', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Pwa not enabled' } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(200) expect(response.json()).to.deep.equal({}) }) it('delivers valid webmanifest with minimal properties', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Valid App Suite', pwa: { enabled: true, shortName: 'Short Name', icon: '/themes/default/logo_512.png', iconWidthHeight: 512 } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(200) expect(response.json()).to.deep.include({ name: 'Short Name', short_name: 'Short Name', icons: [ { src: '/themes/default/logo_512.png', type: 'image/png', sizes: '512x512', purpose: 'any' } ], background_color: 'white' }) }) it('must not deliver an invalid manifest when using the short syntax', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Invalid App Suite', pwa: { enabled: true, name: '123', shortName: true, icon: '/themes/default/logo_512.png', iconWidthHeight: 'noNumbers', backgroundColor: 'hello' } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(500) expect(response.body).to.have.string('Failed to load config for url https://ui-server/pwa.json: Error:') }) it('must not deliver a manifest with invalid host', async function () { const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server-not' } }) expect(response.statusCode).to.equal(500) expect(response.body).to.equal('Failed to load config for url https://ui-server-not/pwa.json: Error: Failed to fetch https://ui-server-not/api/apps/manifests?action=config') }) it('delivers valid webmanifest with raw_manifest', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Valid App Suite', pwa: { enabled: true, raw_manifest: { name: 'Valid App Suite', short_name: 'Valid App Suite', icons: [ { src: '/themes/default/logo_512.png', type: 'image/png', sizes: '512x512', purpose: 'any' } ], theme_color: 'white' } } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(200) expect(response.json()).to.deep.include({ name: 'Valid App Suite', short_name: 'Valid App Suite', icons: [ { src: '/themes/default/logo_512.png', type: 'image/png', sizes: '512x512', purpose: 'any' } ], theme_color: 'white' }) }) it('must not deliver an invalid manifest with raw_manifest', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Invalid App Suite', pwa: { enabled: true, raw_manifest: { name: 123, shortName: 'Invalid App Suite', icons: [ { type: 'image/fff', sizes: 'noNumbers', purpose: 'any' } ], theme_color: 'hello' } } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(500) expect(response.body).to.have.string('Failed to load config for url https://ui-server/pwa.json: Error:') }) it('must choose raw_manifest over short syntax', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Invalid App Suite', pwa: { enabled: true, name: 'Short Syntax', shortName: 'Short Syntax', icon: '/themes/default/logo_512.png', iconWidthHeight: 512, backgroundColor: 'white', raw_manifest: { name: 'Raw Manifest', short_name: 'raw_manifest', icons: [ { src: '/themes/default/logo_512.png', type: 'image/fff', sizes: '123x123', purpose: 'any' } ], theme_color: 'hello' } } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) expect(response.statusCode).to.equal(200) expect(response.json()).to.deep.equal({ name: 'Raw Manifest', short_name: 'raw_manifest', icons: [ { src: '/themes/default/logo_512.png', type: 'image/fff', sizes: '123x123', purpose: 'any' } ], theme_color: 'hello' }) }) it('differ between two hosts', async function () { mockFetch({ 'https://ui-server': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Invalid App Suite', pwa: { enabled: true } } } }, 'https://ui-server-other': { '/api/apps/manifests': { data: { capabilities: [], host: 'all', productName: 'Invalid App Suite', pwa: { enabled: true, shortName: 'Other Suite' } } } } }) const response = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server' } }) const responseOther = await app.inject({ url: '/pwa.json', headers: { host: 'ui-server-other' } }) expect(response.statusCode).to.equal(200) expect(response.json()).to.deep.equal({ name: 'OX App Suite', short_name: 'OX App Suite', icons: [ { src: '/themes/default/logo_512.png', type: 'image/png', sizes: '512x512', purpose: 'any' } ], theme_color: 'white', start_url: '/#pwa=true', display: 'standalone', background_color: 'white', scope: '/', id: '/#pwa=true', protocol_handlers: [ { protocol: 'mailto', url: '/#app=io.ox/mail&mailto=%s' } ] }) expect(responseOther.statusCode).to.equal(200) expect(responseOther.json()).to.deep.equal({ name: 'Other Suite', short_name: 'Other Suite', icons: [ { src: '/themes/default/logo_512.png', type: 'image/png', sizes: '512x512', purpose: 'any' } ], theme_color: 'white', start_url: '/#pwa=true', display: 'standalone', background_color: 'white', scope: '/', id: '/#pwa=true', protocol_handlers: [ { protocol: 'mailto', url: '/#app=io.ox/mail&mailto=%s' } ] }) }) })