github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+incompatible/utils/subpath_test.go (about)

     1  package utils_test
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/mattermost/mattermost-server/model"
    12  	"github.com/mattermost/mattermost-server/utils"
    13  )
    14  
    15  func TestUpdateAssetsSubpath(t *testing.T) {
    16  	t.Run("no client dir", func(t *testing.T) {
    17  		tempDir, err := ioutil.TempDir("", "test_update_assets_subpath")
    18  		require.NoError(t, err)
    19  		defer os.RemoveAll(tempDir)
    20  		os.Chdir(tempDir)
    21  
    22  		err = utils.UpdateAssetsSubpath("/")
    23  		require.Error(t, err)
    24  	})
    25  
    26  	t.Run("valid", func(t *testing.T) {
    27  		tempDir, err := ioutil.TempDir("", "test_update_assets_subpath")
    28  		require.NoError(t, err)
    29  		defer os.RemoveAll(tempDir)
    30  		os.Chdir(tempDir)
    31  
    32  		err = os.Mkdir(model.CLIENT_DIR, 0700)
    33  		require.NoError(t, err)
    34  
    35  		testCases := []struct {
    36  			Description          string
    37  			RootHTML             string
    38  			MainCSS              string
    39  			ManifestJSON         string
    40  			Subpath              string
    41  			ExpectedRootHTML     string
    42  			ExpectedMainCSS      string
    43  			ExpectedManifestJSON string
    44  		}{
    45  			{
    46  				"no changes required, empty subpath provided",
    47  				baseRootHtml,
    48  				baseCss,
    49  				baseManifestJson,
    50  				"",
    51  				baseRootHtml,
    52  				baseCss,
    53  				baseManifestJson,
    54  			},
    55  			{
    56  				"no changes required",
    57  				baseRootHtml,
    58  				baseCss,
    59  				baseManifestJson,
    60  				"/",
    61  				baseRootHtml,
    62  				baseCss,
    63  				baseManifestJson,
    64  			},
    65  			{
    66  				"subpath",
    67  				baseRootHtml,
    68  				baseCss,
    69  				baseManifestJson,
    70  				"/subpath",
    71  				subpathRootHtml,
    72  				subpathCss,
    73  				subpathManifestJson,
    74  			},
    75  			{
    76  				"new subpath from old",
    77  				subpathRootHtml,
    78  				subpathCss,
    79  				subpathManifestJson,
    80  				"/nested/subpath",
    81  				newSubpathRootHtml,
    82  				newSubpathCss,
    83  				newSubpathManifestJson,
    84  			},
    85  			{
    86  				"resetting to /",
    87  				subpathRootHtml,
    88  				subpathCss,
    89  				baseManifestJson,
    90  				"/",
    91  				resetRootHtml,
    92  				baseCss,
    93  				baseManifestJson,
    94  			},
    95  		}
    96  
    97  		for _, testCase := range testCases {
    98  			t.Run(testCase.Description, func(t *testing.T) {
    99  				ioutil.WriteFile(filepath.Join(tempDir, model.CLIENT_DIR, "root.html"), []byte(testCase.RootHTML), 0700)
   100  				ioutil.WriteFile(filepath.Join(tempDir, model.CLIENT_DIR, "main.css"), []byte(testCase.MainCSS), 0700)
   101  				ioutil.WriteFile(filepath.Join(tempDir, model.CLIENT_DIR, "manifest.json"), []byte(testCase.ManifestJSON), 0700)
   102  				err := utils.UpdateAssetsSubpath(testCase.Subpath)
   103  				require.NoError(t, err)
   104  
   105  				contents, err := ioutil.ReadFile(filepath.Join(tempDir, model.CLIENT_DIR, "root.html"))
   106  				require.NoError(t, err)
   107  				require.Equal(t, testCase.ExpectedRootHTML, string(contents))
   108  
   109  				contents, err = ioutil.ReadFile(filepath.Join(tempDir, model.CLIENT_DIR, "main.css"))
   110  				require.NoError(t, err)
   111  				require.Equal(t, testCase.ExpectedMainCSS, string(contents))
   112  
   113  				contents, err = ioutil.ReadFile(filepath.Join(tempDir, model.CLIENT_DIR, "manifest.json"))
   114  				require.NoError(t, err)
   115  				require.Equal(t, testCase.ExpectedManifestJSON, string(contents))
   116  			})
   117  		}
   118  	})
   119  }
   120  
   121  func TestGetSubpathFromConfig(t *testing.T) {
   122  	sToP := func(s string) *string {
   123  		return &s
   124  	}
   125  
   126  	testCases := []struct {
   127  		Description     string
   128  		SiteURL         *string
   129  		ExpectedError   bool
   130  		ExpectedSubpath string
   131  	}{
   132  		{
   133  			"empty SiteURL",
   134  			sToP(""),
   135  			false,
   136  			"/",
   137  		},
   138  		{
   139  			"invalid SiteURL",
   140  			sToP("cache_object:foo/bar"),
   141  			true,
   142  			"",
   143  		},
   144  		{
   145  			"nil SiteURL",
   146  			nil,
   147  			false,
   148  			"/",
   149  		},
   150  		{
   151  			"no trailing slash",
   152  			sToP("http://localhost:8065"),
   153  			false,
   154  			"/",
   155  		},
   156  		{
   157  			"trailing slash",
   158  			sToP("http://localhost:8065/"),
   159  			false,
   160  			"/",
   161  		},
   162  		{
   163  			"subpath, no trailing slash",
   164  			sToP("http://localhost:8065/subpath"),
   165  			false,
   166  			"/subpath",
   167  		},
   168  		{
   169  			"trailing slash",
   170  			sToP("http://localhost:8065/subpath/"),
   171  			false,
   172  			"/subpath",
   173  		},
   174  	}
   175  
   176  	for _, testCase := range testCases {
   177  		t.Run(testCase.Description, func(t *testing.T) {
   178  			config := &model.Config{
   179  				ServiceSettings: model.ServiceSettings{
   180  					SiteURL: testCase.SiteURL,
   181  				},
   182  			}
   183  
   184  			subpath, err := utils.GetSubpathFromConfig(config)
   185  			if testCase.ExpectedError {
   186  				require.Error(t, err)
   187  			} else {
   188  				require.NoError(t, err)
   189  			}
   190  
   191  			require.Equal(t, testCase.ExpectedSubpath, subpath)
   192  		})
   193  	}
   194  }
   195  
   196  const baseRootHtml = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv=Content-Security-Policy content="script-src 'self' cdn.segment.com/analytics.js/ 'unsafe-eval'"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style> <link href="/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
   197  
   198  const baseCss = `@font-face{font-family:FontAwesome;src:url(/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
   199  
   200  const subpathRootHtml = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv=Content-Security-Policy content="script-src 'self' cdn.segment.com/analytics.js/ 'unsafe-eval' 'sha256-tPOjw+tkVs9axL78ZwGtYl975dtyPHB6LYKAO2R3gR4='"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/subpath/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/subpath/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/subpath/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/subpath/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/subpath/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/subpath/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/subpath/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/subpath/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/subpath/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/subpath/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/subpath/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/subpath/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style><script>window.publicPath='/subpath/static/'</script> <link href="/subpath/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/subpath/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
   201  
   202  const subpathCss = `@font-face{font-family:FontAwesome;src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
   203  
   204  const newSubpathRootHtml = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv=Content-Security-Policy content="script-src 'self' cdn.segment.com/analytics.js/ 'unsafe-eval' 'sha256-mbRaPRRpWz6MNkX9SyXWMJ8XnWV4w/DoqK2M0ryUAvc='"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/nested/subpath/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/nested/subpath/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/nested/subpath/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/nested/subpath/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/nested/subpath/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/nested/subpath/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/nested/subpath/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/nested/subpath/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/nested/subpath/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/nested/subpath/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/nested/subpath/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/nested/subpath/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style><script>window.publicPath='/nested/subpath/static/'</script> <link href="/nested/subpath/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/nested/subpath/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
   205  
   206  const newSubpathCss = `@font-face{font-family:FontAwesome;src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot);src:url(/nested/subpath/static/files/674f50d287a8c48dc19ba404d20fe713.eot?#iefix&v=4.7.0) format("embedded-opentype"),url(/nested/subpath/static/files/af7ae505a9eed503f8b8e6982036873e.woff2) format("woff2"),url(/nested/subpath/static/files/fee66e712a8a08eef5805a46892932ad.woff) format("woff"),url(/nested/subpath/static/files/b06871f281fee6b241d60582ae9369b9.ttf) format("truetype"),url(/nested/subpath/static/files/677433a0892aaed7b7d2628c313c9775.svg#fontawesomeregular) format("svg");font-weight:400;font-style:normal}`
   207  
   208  const resetRootHtml = `<!DOCTYPE html> <html lang=en> <head> <meta charset=utf-8> <meta http-equiv=Content-Security-Policy content="script-src 'self' cdn.segment.com/analytics.js/ 'unsafe-eval' 'sha256-VFw7U/t/OI+I9YMja3c2GDwEQbnlOq/L5+GealgesK8='"> <meta http-equiv=X-UA-Compatible content="IE=edge"> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"> <meta name=robots content="noindex, nofollow"> <meta name=referrer content=no-referrer> <title>Mattermost</title> <meta name=apple-mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-status-bar-style content=default> <meta name=mobile-web-app-capable content=yes> <meta name=apple-mobile-web-app-title content=Mattermost> <meta name=application-name content=Mattermost> <meta name=format-detection content="telephone=no"> <link rel=apple-touch-icon sizes=57x57 href=/static/files/78b7e73b41b8731ce2c41c870ecc8886.png> <link rel=apple-touch-icon sizes=60x60 href=/static/files/51d00ffd13afb6d74fd8f6dfdeef768a.png> <link rel=apple-touch-icon sizes=72x72 href=/static/files/23645596f8f78f017bd4d457abb855c4.png> <link rel=apple-touch-icon sizes=76x76 href=/static/files/26e9d72f472663a00b4b206149459fab.png> <link rel=apple-touch-icon sizes=144x144 href=/static/files/7bd91659bf3fc8c68fcd45fc1db9c630.png> <link rel=apple-touch-icon sizes=120x120 href=/static/files/fa69ffe11eb334aaef5aece8d848ca62.png> <link rel=apple-touch-icon sizes=152x152 href=/static/files/f046777feb6ab12fc43b8f9908b1db35.png> <link rel=icon type=image/png sizes=16x16 href=/static/files/02b96247d275680adaaabf01c71c571d.png> <link rel=icon type=image/png sizes=32x32 href=/static/files/1d9020f201a6762421cab8d30624fdd8.png> <link rel=icon type=image/png sizes=96x96 href=/static/files/fe23af39ae98d77dc26ae8586565970f.png> <link rel=icon type=image/png sizes=192x192 href=/static/files/d7ff68a7675f84337cc154c3d4abe713.png> <link rel=manifest href=/static/files/a985ad72552ad069537d6eea81e719c7.json> <link rel=stylesheet class=code_theme> <style>.error-screen{font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding-top:50px;max-width:750px;font-size:14px;color:#333;margin:auto;display:none;line-height:1.5}.error-screen h2{font-size:30px;font-weight:400;line-height:1.2}.error-screen ul{padding-left:15px;line-height:1.7;margin-top:0;margin-bottom:10px}.error-screen hr{color:#ddd;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.error-screen-visible{display:block}</style><script>window.publicPath='/static/'</script> <link href="/static/main.364fd054d7a6d741efc6.css" rel="stylesheet"><script type="text/javascript" src="/static/main.e49599ac425584ffead5.js"></script></head> <body class=font--open_sans> <div id=root> <div class=error-screen> <h2>Cannot connect to Mattermost</h2> <hr/> <p>We're having trouble connecting to Mattermost. If refreshing this page (Ctrl+R or Command+R) does not work, please verify that your computer is connected to the internet.</p> <br/> </div> <div class=loading-screen style=position:relative> <div class=loading__content> <div class="round round-1"></div> <div class="round round-2"></div> <div class="round round-3"></div> </div> </div> </div> <noscript> To use Mattermost, please enable JavaScript. </noscript> </body> </html>`
   209  
   210  const baseManifestJson = `{
   211    "icons": [
   212      {
   213        "src": "/static/icon_96x96.png",
   214        "sizes": "96x96",
   215        "type": "image/png"
   216      },
   217      {
   218        "src": "/static/icon_32x32.png",
   219        "sizes": "32x32",
   220        "type": "image/png"
   221      },
   222      {
   223        "src": "/static/icon_16x16.png",
   224        "sizes": "16x16",
   225        "type": "image/png"
   226      },
   227      {
   228        "src": "/static/icon_76x76.png",
   229        "sizes": "76x76",
   230        "type": "image/png"
   231      },
   232      {
   233        "src": "/static/icon_72x72.png",
   234        "sizes": "72x72",
   235        "type": "image/png"
   236      },
   237      {
   238        "src": "/static/icon_60x60.png",
   239        "sizes": "60x60",
   240        "type": "image/png"
   241      },
   242      {
   243        "src": "/static/icon_57x57.png",
   244        "sizes": "57x57",
   245        "type": "image/png"
   246      },
   247      {
   248        "src": "/static/icon_152x152.png",
   249        "sizes": "152x152",
   250        "type": "image/png"
   251      },
   252      {
   253        "src": "/static/icon_144x144.png",
   254        "sizes": "144x144",
   255        "type": "image/png"
   256      },
   257      {
   258        "src": "/static/icon_120x120.png",
   259        "sizes": "120x120",
   260        "type": "image/png"
   261      },
   262      {
   263        "src": "/static/icon_192x192.png",
   264        "sizes": "192x192",
   265        "type": "image/png"
   266      }
   267    ],
   268    "name": "Mattermost",
   269    "short_name": "Mattermost",
   270    "orientation": "any",
   271    "display": "standalone",
   272    "start_url": ".",
   273    "description": "Mattermost is an open source, self-hosted Slack-alternative",
   274    "background_color": "#ffffff"
   275  }
   276  `
   277  
   278  const subpathManifestJson = `{
   279    "icons": [
   280      {
   281        "src": "/subpath/static/icon_96x96.png",
   282        "sizes": "96x96",
   283        "type": "image/png"
   284      },
   285      {
   286        "src": "/subpath/static/icon_32x32.png",
   287        "sizes": "32x32",
   288        "type": "image/png"
   289      },
   290      {
   291        "src": "/subpath/static/icon_16x16.png",
   292        "sizes": "16x16",
   293        "type": "image/png"
   294      },
   295      {
   296        "src": "/subpath/static/icon_76x76.png",
   297        "sizes": "76x76",
   298        "type": "image/png"
   299      },
   300      {
   301        "src": "/subpath/static/icon_72x72.png",
   302        "sizes": "72x72",
   303        "type": "image/png"
   304      },
   305      {
   306        "src": "/subpath/static/icon_60x60.png",
   307        "sizes": "60x60",
   308        "type": "image/png"
   309      },
   310      {
   311        "src": "/subpath/static/icon_57x57.png",
   312        "sizes": "57x57",
   313        "type": "image/png"
   314      },
   315      {
   316        "src": "/subpath/static/icon_152x152.png",
   317        "sizes": "152x152",
   318        "type": "image/png"
   319      },
   320      {
   321        "src": "/subpath/static/icon_144x144.png",
   322        "sizes": "144x144",
   323        "type": "image/png"
   324      },
   325      {
   326        "src": "/subpath/static/icon_120x120.png",
   327        "sizes": "120x120",
   328        "type": "image/png"
   329      },
   330      {
   331        "src": "/subpath/static/icon_192x192.png",
   332        "sizes": "192x192",
   333        "type": "image/png"
   334      }
   335    ],
   336    "name": "Mattermost",
   337    "short_name": "Mattermost",
   338    "orientation": "any",
   339    "display": "standalone",
   340    "start_url": ".",
   341    "description": "Mattermost is an open source, self-hosted Slack-alternative",
   342    "background_color": "#ffffff"
   343  }
   344  `
   345  
   346  const newSubpathManifestJson = `{
   347    "icons": [
   348      {
   349        "src": "/nested/subpath/static/icon_96x96.png",
   350        "sizes": "96x96",
   351        "type": "image/png"
   352      },
   353      {
   354        "src": "/nested/subpath/static/icon_32x32.png",
   355        "sizes": "32x32",
   356        "type": "image/png"
   357      },
   358      {
   359        "src": "/nested/subpath/static/icon_16x16.png",
   360        "sizes": "16x16",
   361        "type": "image/png"
   362      },
   363      {
   364        "src": "/nested/subpath/static/icon_76x76.png",
   365        "sizes": "76x76",
   366        "type": "image/png"
   367      },
   368      {
   369        "src": "/nested/subpath/static/icon_72x72.png",
   370        "sizes": "72x72",
   371        "type": "image/png"
   372      },
   373      {
   374        "src": "/nested/subpath/static/icon_60x60.png",
   375        "sizes": "60x60",
   376        "type": "image/png"
   377      },
   378      {
   379        "src": "/nested/subpath/static/icon_57x57.png",
   380        "sizes": "57x57",
   381        "type": "image/png"
   382      },
   383      {
   384        "src": "/nested/subpath/static/icon_152x152.png",
   385        "sizes": "152x152",
   386        "type": "image/png"
   387      },
   388      {
   389        "src": "/nested/subpath/static/icon_144x144.png",
   390        "sizes": "144x144",
   391        "type": "image/png"
   392      },
   393      {
   394        "src": "/nested/subpath/static/icon_120x120.png",
   395        "sizes": "120x120",
   396        "type": "image/png"
   397      },
   398      {
   399        "src": "/nested/subpath/static/icon_192x192.png",
   400        "sizes": "192x192",
   401        "type": "image/png"
   402      }
   403    ],
   404    "name": "Mattermost",
   405    "short_name": "Mattermost",
   406    "orientation": "any",
   407    "display": "standalone",
   408    "start_url": ".",
   409    "description": "Mattermost is an open source, self-hosted Slack-alternative",
   410    "background_color": "#ffffff"
   411  }
   412  `