1package sourcemap_test 2 3import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "strings" 9 "testing" 10 11 "github.com/go-sourcemap/sourcemap" 12) 13 14const jqSourceMapURL = "http://code.jquery.com/jquery-2.0.3.min.map" 15 16var jqSourceMapBytes []byte 17 18func init() { 19 resp, err := http.Get(jqSourceMapURL) 20 if err != nil { 21 panic(err) 22 } 23 defer resp.Body.Close() 24 25 jqSourceMapBytes, err = ioutil.ReadAll(resp.Body) 26 if err != nil { 27 panic(err) 28 } 29} 30 31type sourceMapTest struct { 32 genLine int 33 genColumn int 34 wantedSource string 35 wantedName string 36 wantedLine int 37 wantedColumn int 38} 39 40func (test *sourceMapTest) String() string { 41 return fmt.Sprintf("line=%d col=%d in file=%s", test.genLine, test.genColumn, test.wantedSource) 42} 43 44func (test *sourceMapTest) assert(t *testing.T, smap *sourcemap.Consumer) { 45 source, name, line, col, ok := smap.Source(test.genLine, test.genColumn) 46 if !ok { 47 if test.wantedSource == "" && 48 test.wantedName == "" && 49 test.wantedLine == 0 && 50 test.wantedColumn == 0 { 51 return 52 } 53 t.Fatalf("Source not found for %s", test) 54 } 55 if source != test.wantedSource { 56 t.Fatalf("file: got %q, wanted %q (%s)", source, test.wantedSource, test) 57 } 58 if name != test.wantedName { 59 t.Fatalf("func: got %q, wanted %q (%s)", name, test.wantedName, test) 60 } 61 if line != test.wantedLine { 62 t.Fatalf("line: got %d, wanted %d (%s)", line, test.wantedLine, test) 63 } 64 if col != test.wantedColumn { 65 t.Fatalf("column: got %d, wanted %d (%s)", col, test.wantedColumn, test) 66 } 67} 68 69func TestSourceMap(t *testing.T) { 70 testSourceMap(t, sourceMapJSON) 71} 72 73func TestIndexedSourceMap(t *testing.T) { 74 testSourceMap(t, indexedSourceMapJSON) 75} 76 77func testSourceMap(t *testing.T, json string) { 78 smap, err := sourcemap.Parse("", []byte(json)) 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 tests := []sourceMapTest{ 84 {1, 1, "/the/root/one.js", "", 1, 1}, 85 {1, 5, "/the/root/one.js", "", 1, 5}, 86 {1, 9, "/the/root/one.js", "", 1, 11}, 87 {1, 18, "/the/root/one.js", "bar", 1, 21}, 88 {1, 21, "/the/root/one.js", "", 2, 3}, 89 {1, 28, "/the/root/one.js", "baz", 2, 10}, 90 {1, 32, "/the/root/one.js", "bar", 2, 14}, 91 92 {2, 1, "/the/root/two.js", "", 1, 1}, 93 {2, 5, "/the/root/two.js", "", 1, 5}, 94 {2, 9, "/the/root/two.js", "", 1, 11}, 95 {2, 18, "/the/root/two.js", "n", 1, 21}, 96 {2, 21, "/the/root/two.js", "", 2, 3}, 97 {2, 28, "/the/root/two.js", "n", 2, 10}, 98 99 // Fuzzy match. 100 {1, 20, "/the/root/one.js", "bar", 1, 21}, 101 {1, 30, "/the/root/one.js", "baz", 2, 10}, 102 {2, 12, "/the/root/two.js", "", 1, 11}, 103 } 104 for i := range tests { 105 tests[i].assert(t, smap) 106 } 107 108 content := smap.SourceContent("/the/root/one.js") 109 if content != oneSourceContent { 110 t.Fatalf("%q != %q", content, oneSourceContent) 111 } 112 113 content = smap.SourceContent("/the/root/two.js") 114 if content != twoSourceContent { 115 t.Fatalf("%q != %q", content, twoSourceContent) 116 } 117 118 _, _, _, _, ok := smap.Source(3, 0) 119 if ok { 120 t.Fatal("source must not exist") 121 } 122} 123 124func TestSourceRootURL(t *testing.T) { 125 jsonStr := sourceMapJSON 126 jsonStr = strings.Replace(jsonStr, "/the/root", "http://the/root", 1) 127 jsonStr = strings.Replace(jsonStr, "one.js", "../one.js", 1) 128 129 smap, err := sourcemap.Parse("", []byte(jsonStr)) 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 tests := []*sourceMapTest{ 135 {1, 1, "http://the/one.js", "", 1, 1}, 136 {2, 1, "http://the/root/two.js", "", 1, 1}, 137 } 138 for _, test := range tests { 139 test.assert(t, smap) 140 } 141} 142 143func TestEmptySourceRootURL(t *testing.T) { 144 jsonStr := sourceMapJSON 145 jsonStr = strings.Replace(jsonStr, "/the/root", "", 1) 146 jsonStr = strings.Replace(jsonStr, "one.js", "../one.js", 1) 147 148 smap, err := sourcemap.Parse("http://the/root/app.min.map", []byte(jsonStr)) 149 if err != nil { 150 t.Fatal(err) 151 } 152 153 tests := []*sourceMapTest{ 154 {1, 1, "http://the/one.js", "", 1, 1}, 155 {2, 1, "http://the/root/two.js", "", 1, 1}, 156 } 157 for _, test := range tests { 158 test.assert(t, smap) 159 } 160} 161 162func TestAbsSourceURL(t *testing.T) { 163 jsonStr := sourceMapJSON 164 jsonStr = strings.Replace(jsonStr, "/the/root", "", 1) 165 jsonStr = strings.Replace(jsonStr, "one.js", "http://the/root/one.js", 1) 166 jsonStr = strings.Replace(jsonStr, "two.js", "/another/root/two.js", 1) 167 168 testAbsSourceURL(t, "", jsonStr) 169 testAbsSourceURL(t, "http://path/to/map", jsonStr) 170} 171 172func testAbsSourceURL(t *testing.T, mapURL, jsonStr string) { 173 smap, err := sourcemap.Parse(mapURL, []byte(jsonStr)) 174 if err != nil { 175 t.Fatal(err) 176 } 177 178 tests := []*sourceMapTest{ 179 {1, 1, "http://the/root/one.js", "", 1, 1}, 180 {2, 1, "/another/root/two.js", "", 1, 1}, 181 } 182 for _, test := range tests { 183 test.assert(t, smap) 184 } 185} 186 187func TestJQuerySourceMap(t *testing.T) { 188 smap, err := sourcemap.Parse(jqSourceMapURL, jqSourceMapBytes) 189 if err != nil { 190 t.Fatal(err) 191 } 192 193 tests := []*sourceMapTest{ 194 {1, 1, "", "", 0, 0}, 195 {4, 0, "", "", 0, 0}, 196 {4, 1, "http://code.jquery.com/jquery-2.0.3.js", "", 14, 0}, 197 {4, 10, "http://code.jquery.com/jquery-2.0.3.js", "window", 14, 11}, 198 {5, 6789, "http://code.jquery.com/jquery-2.0.3.js", "apply", 4360, 27}, 199 {5, 10006, "http://code.jquery.com/jquery-2.0.3.js", "apply", 4676, 8}, 200 {4, 553, "http://code.jquery.com/jquery-2.0.3.js", "ready", 93, 9}, 201 {999999, 0, "", "", 0, 0}, 202 } 203 for _, test := range tests { 204 test.assert(t, smap) 205 } 206} 207 208// https://github.com/mozilla/source-map/blob/master/test/util.js 209// 210// This is a test mapping which maps functions from two different files 211// (one.js and two.js) to a minified generated source. 212// 213// Here is one.js: 214// 215// ONE.foo = function (bar) { 216// return baz(bar); 217// }; 218// 219// Here is two.js: 220// 221// TWO.inc = function (n) { 222// return n + 1; 223// }; 224// 225// And here is the generated code (min.js): 226// 227// ONE.foo=function(a){return baz(a);}; 228// TWO.inc=function(a){return a+1;}; 229 230const genCode = `exports.testGeneratedCode = "ONE.foo=function(a){return baz(a);}; 231TWO.inc=function(a){return a+1;};` 232 233var oneSourceContent = `ONE.foo = function (bar) { 234 return baz(bar); 235};` 236 237var twoSourceContent = `TWO.inc = function (n) { 238 return n + 1; 239};` 240 241var sourceMapJSON = `{ 242 "version": 3, 243 "file": "min.js", 244 "sources": ["one.js", "two.js"], 245 "sourcesContent": ` + j([]string{oneSourceContent, twoSourceContent}) + `, 246 "sourceRoot": "/the/root", 247 "names": ["bar", "baz", "n"], 248 "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA" 249}` 250 251func j(v interface{}) string { 252 b, _ := json.Marshal(v) 253 return string(b) 254} 255 256var indexedSourceMapJSON = `{ 257 "version": 3, 258 "file": "min.js", 259 "sections": [{ 260 "offset": {"line": 0, "column": 0}, 261 "map": { 262 "version": 3, 263 "file": "min.js", 264 "sources": ["one.js"], 265 "sourcesContent": ` + j([]string{oneSourceContent}) + `, 266 "sourceRoot": "/the/root", 267 "names": ["bar", "baz"], 268 "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID" 269 } 270 }, { 271 "offset": {"line": 1, "column": 0}, 272 "map": { 273 "version": 3, 274 "file": "min.js", 275 "sources": ["two.js"], 276 "sourcesContent": ` + j([]string{twoSourceContent}) + `, 277 "sourceRoot": "/the/root", 278 "names": ["n"], 279 "mappings": "CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOA" 280 } 281 }] 282}` 283