eintopf.info@v0.13.16/web/event_test.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package web 17 18 import ( 19 "context" 20 "fmt" 21 "net/url" 22 "regexp" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/PuerkitoBio/goquery" 28 "github.com/google/go-cmp/cmp" 29 "github.com/iand/microdata" 30 31 "eintopf.info/service/event" 32 "eintopf.info/service/group" 33 "eintopf.info/service/place" 34 "eintopf.info/service/revent" 35 ) 36 37 func TestEventPage(t *testing.T) { 38 tests := []routeTest{ 39 { 40 name: "NotFound", 41 tz: time.UTC, 42 uri: "/event/12345", 43 events: []*event.NewEvent{ 44 { 45 Published: true, 46 Name: "foo", 47 }, 48 }, 49 status: 404, 50 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 51 assertNodes(t, doc, ".error", []string{"404 Veranstaltung wurde nicht gefunden"}) 52 }, 53 }, 54 { 55 name: "NotPublished", 56 tz: time.UTC, 57 uri: "/event/0", 58 events: []*event.NewEvent{ 59 { 60 Published: false, 61 Name: "foo", 62 }, 63 }, 64 status: 200, 65 }, 66 { 67 name: "MetaNoIndexNotPublished", 68 tz: time.UTC, 69 uri: "/event/0", 70 events: []*event.NewEvent{ 71 { 72 Published: false, 73 Name: "foo", 74 }, 75 }, 76 status: 200, 77 htmlTestFunc: func(tt *testing.T, doc *goquery.Document) { 78 assertRobotsIndex(t, doc, "noindex, nofollow") 79 }, 80 }, 81 { 82 name: "MetaIndexPublished", 83 tz: time.UTC, 84 uri: "/event/0", 85 events: []*event.NewEvent{ 86 { 87 Published: true, 88 Name: "foo", 89 }, 90 }, 91 status: 200, 92 htmlTestFunc: func(tt *testing.T, doc *goquery.Document) { 93 assertRobotsIndex(t, doc, "index, follow") 94 }, 95 }, 96 { 97 name: "MetaDefault", 98 tz: time.UTC, 99 uri: "/event/0", 100 themes: []string{"eintopf", "testdata/themes/meta"}, 101 events: []*event.NewEvent{ 102 { 103 Published: true, 104 Name: "Foo", 105 }, 106 }, 107 status: 200, 108 htmlTestFunc: func(tt *testing.T, doc *goquery.Document) { 109 assertRobotsIndex(t, doc, "index, follow") 110 assertMeta(t, doc, MetaTags{ 111 Title: "Foo | Custom Sitename", 112 Description: "Custom Meta Description", 113 Image: "/assets/images/logo.png", 114 }) 115 }, 116 }, 117 { 118 name: "MetaContent", 119 tz: time.UTC, 120 uri: "/event/0", 121 events: []*event.NewEvent{ 122 { 123 Published: true, 124 Name: "Foo", 125 Description: "Foo bar", 126 Image: "Foobar.png", 127 }, 128 }, 129 status: 200, 130 htmlTestFunc: func(tt *testing.T, doc *goquery.Document) { 131 assertRobotsIndex(t, doc, "index, follow") 132 assertMeta(t, doc, MetaTags{ 133 Title: "Foo | Eintopf Stuttgart", 134 Description: "Foo bar", 135 Image: "/api/v1/media/Foobar.png", 136 }) 137 }, 138 }, 139 { 140 name: "Schema.orgEventMinimal", 141 tz: time.UTC, 142 uri: "/event/0", 143 events: []*event.NewEvent{ 144 { 145 Published: true, 146 Name: "Foo", 147 Description: "Foo bar", 148 Start: time.Date(2022, 10, 5, 14, 30, 0, 0, time.UTC), 149 }, 150 }, 151 status: 200, 152 htmlTestFunc: func(tt *testing.T, doc *goquery.Document) { 153 var html, err = doc.Html() 154 if err != nil { 155 tt.Errorf(err.Error()) 156 } 157 158 baseUrl, _ := url.Parse("http://localhost/") 159 p := microdata.NewParser(strings.NewReader(html), baseUrl) 160 data, err := p.Parse() 161 if err != nil { 162 tt.Errorf(err.Error()) 163 } 164 165 pageMicrodataJSON, err := data.JSON() 166 if err != nil { 167 t.Errorf(`JSON marshal failed for pageMicrodataJSON`) 168 } 169 170 testMicrodataItem := microdata.NewItem() 171 testMicrodataItem.AddType("https://schema.org/Event") 172 testMicrodataItem.AddString("name", "Foo") 173 testMicrodataItem.AddString("description", "Foo bar") 174 testMicrodataItem.AddString("startDate", "2022-10-05T14:30:00Z") 175 testMicrodata := microdata.NewMicrodata() 176 testMicrodata.AddItem(testMicrodataItem) 177 testMicrodataJSON, err := testMicrodata.JSON() 178 if err != nil { 179 t.Errorf(`JSON marshal failed for testMicrodataJSON`) 180 } 181 182 if diff := cmp.Diff(string(testMicrodataJSON), string(pageMicrodataJSON)); diff != "" { 183 t.Errorf(`schema event content mismatch (-want +got):\n%s`, diff) 184 } 185 }, 186 }, 187 { 188 name: "Schema.orgEventMaximum", 189 tz: time.UTC, 190 uri: "/event/0", 191 events: []*event.NewEvent{ 192 { 193 Published: true, 194 Name: "Foo", 195 Description: "Foo bar", 196 Start: time.Date(2022, 10, 5, 14, 30, 0, 0, time.UTC), 197 End: tptr(time.Date(2022, 10, 5, 20, 30, 0, 0, time.UTC)), 198 Location: "bar", 199 Organizers: []string{"dada"}, 200 }, 201 }, 202 status: 200, 203 htmlTestFunc: func(tt *testing.T, doc *goquery.Document) { 204 var html, err = doc.Html() 205 if err != nil { 206 tt.Errorf(err.Error()) 207 } 208 209 baseUrl, _ := url.Parse("http://localhost/") 210 p := microdata.NewParser(strings.NewReader(html), baseUrl) 211 data, err := p.Parse() 212 if err != nil { 213 tt.Errorf(err.Error()) 214 } 215 216 pageMicrodataJSON, err := data.JSON() 217 if err != nil { 218 t.Errorf(`JSON marshal failed for pageMicrodataJSON`) 219 } 220 221 testMicrodataItemLocation := microdata.NewItem() 222 testMicrodataItemLocation.AddType("https://schema.org/Place") 223 testMicrodataItemLocation.AddString("name", "bar") 224 225 testMicrodataItem := microdata.NewItem() 226 testMicrodataItem.AddType("https://schema.org/Event") 227 testMicrodataItem.AddString("name", "Foo") 228 testMicrodataItem.AddString("description", "Foo bar") 229 testMicrodataItem.AddString("startDate", "2022-10-05T14:30:00Z") 230 testMicrodataItem.AddString("endDate", "2022-10-05T20:30:00Z") 231 testMicrodataItem.AddItem("location", testMicrodataItemLocation) 232 testMicrodataItem.AddString("organizer", "dada") 233 234 testMicrodata := microdata.NewMicrodata() 235 testMicrodata.AddItem(testMicrodataItem) 236 testMicrodataJSON, err := testMicrodata.JSON() 237 if err != nil { 238 t.Errorf(`JSON marshal failed for testMicrodataJSON`) 239 } 240 241 if diff := cmp.Diff(string(testMicrodataJSON), string(pageMicrodataJSON)); diff != "" { 242 t.Errorf(`schema event content mismatch (-want +got):\n%s`, diff) 243 } 244 }, 245 }, 246 { 247 name: "Canceled", 248 tz: time.UTC, 249 uri: "/event/0", 250 events: []*event.NewEvent{ 251 { 252 Published: true, 253 Name: "foo", 254 }, 255 }, 256 transform: func(eventService event.Storer, groupService group.Service, placeService place.Service) error { 257 _, err := eventService.Update(context.Background(), &event.Event{ 258 ID: "0", 259 Published: true, 260 Canceled: true, 261 Name: "foo", 262 }) 263 return err 264 }, 265 status: 200, 266 contentTestFunc: func(t *testing.T, content string) { 267 if !strings.Contains(content, "Fällt aus") { 268 t.Error("should contain 'Fällt aus'") 269 } 270 }, 271 }, 272 { 273 name: "NoEndDate", 274 tz: time.UTC, 275 uri: "/event/0", 276 events: []*event.NewEvent{ 277 { 278 Published: true, 279 Name: "foo", 280 Start: time.Date(2022, 10, 5, 14, 30, 0, 0, time.UTC), 281 }, 282 }, 283 status: 200, 284 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 285 assertInfoDate(t, doc, "05.10.2022 14:30 Uhr") 286 }, 287 }, 288 { 289 name: "WithEndDateSameDay", 290 tz: time.UTC, 291 uri: "/event/0", 292 events: []*event.NewEvent{ 293 { 294 Published: true, 295 Name: "foo", 296 Start: time.Date(2022, 10, 5, 14, 30, 0, 0, time.UTC), 297 End: tptr(time.Date(2022, 10, 5, 16, 30, 0, 0, time.UTC)), 298 }, 299 }, 300 status: 200, 301 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 302 assertInfoDate(t, doc, "05.10.2022 14:30 Uhr - 16:30 Uhr") 303 }, 304 }, 305 { 306 name: "WithEndDateNotSameDay", 307 tz: time.UTC, 308 uri: "/event/0", 309 events: []*event.NewEvent{ 310 { 311 Published: true, 312 Name: "foo", 313 Start: time.Date(2022, 10, 5, 14, 30, 0, 0, time.UTC), 314 End: tptr(time.Date(2022, 10, 7, 16, 30, 0, 0, time.UTC)), 315 }, 316 }, 317 status: 200, 318 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 319 assertInfoDate(t, doc, "05.10.2022 14:30 Uhr - 07.10.2022 16:30 Uhr") 320 }, 321 }, 322 { 323 name: "NoOrganizers", 324 tz: time.UTC, 325 uri: "/event/0", 326 events: []*event.NewEvent{ 327 { 328 Published: true, 329 Name: "foo", 330 Organizers: []string{}, 331 }, 332 }, 333 status: 200, 334 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 335 assertInfoOrganizers(t, doc, "") 336 }, 337 }, 338 { 339 name: "OneOrganizer", 340 tz: time.UTC, 341 uri: "/event/0", 342 events: []*event.NewEvent{ 343 { 344 Published: true, 345 Name: "foo", 346 Organizers: []string{"dada"}, 347 }, 348 }, 349 status: 200, 350 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 351 assertInfoOrganizers(t, doc, "dada") 352 }, 353 }, 354 { 355 name: "MultipleOrganizer", 356 tz: time.UTC, 357 uri: "/event/0", 358 events: []*event.NewEvent{ 359 { 360 Published: true, 361 Name: "foo", 362 Organizers: []string{"dada", "baba"}, 363 }, 364 }, 365 status: 200, 366 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 367 assertInfoOrganizers(t, doc, "dada, baba") 368 }, 369 }, 370 { 371 name: "Involved", 372 tz: time.UTC, 373 uri: "/event/0", 374 events: []*event.NewEvent{ 375 { 376 Published: true, 377 Name: "foo", 378 Involved: []event.Involved{{Name: "foo", Description: "descfoo"}, {Name: "bar", Description: "descbar"}}, 379 }, 380 }, 381 status: 200, 382 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 383 elementsName := doc.Find(`.info-involved .info-data`) 384 elementsDescription := doc.Find(`.description`) 385 if elementsName.Length() != 2 { 386 t.Errorf(`Expected 2 involved, got %d`, elementsName.Length()) 387 } 388 if diff := cmp.Diff("foobar", elementsName.Text()); diff != "" { 389 t.Errorf(`Involved content mismatch (-want +got):\n%s`, diff) 390 } 391 if elementsDescription.Length() != 2 { 392 t.Errorf(`Expected 2 involved descriptions, got %d`, elementsDescription.Length()) 393 } 394 if diff := cmp.Diff("descfoodescbar", elementsDescription.Text()); diff != "" { 395 t.Errorf(`Involved description content mismatch (-want +got):\n%s`, diff) 396 } 397 }, 398 }, 399 { 400 name: "Location", 401 tz: time.UTC, 402 uri: "/event/0", 403 events: []*event.NewEvent{ 404 { 405 Published: true, 406 Name: "foo", 407 Location: "bar", 408 }, 409 }, 410 status: 200, 411 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 412 assertInfoLocation(t, doc, "bar", "") 413 }, 414 }, 415 { 416 name: "LocationAndLocation2", 417 tz: time.UTC, 418 uri: "/event/0", 419 events: []*event.NewEvent{ 420 { 421 Published: true, 422 Name: "foo", 423 Location: "bar", 424 Location2: "bar2", 425 }, 426 }, 427 status: 200, 428 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 429 assertInfoLocation(t, doc, "bar", "bar2") 430 }, 431 }, 432 { 433 name: "Children", 434 tz: time.UTC, 435 uri: "/event/0", 436 events: []*event.NewEvent{ 437 { 438 Published: true, 439 Name: "foo", 440 }, 441 { 442 Parent: "id:0", 443 Published: true, 444 Name: "bar", 445 Start: time.Date(2022, 9, 8, 10, 0, 0, 0, time.UTC), 446 }, 447 { 448 Parent: "id:0", 449 Published: true, 450 Name: "baz", 451 Start: time.Date(2022, 9, 9, 10, 0, 0, 0, time.UTC), 452 }, 453 }, 454 status: 200, 455 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 456 assertNodes(t, doc, "h2", []string{"Programm 08. Sep / Donnerstag", "Programm 09. Sep / Freitag"}) 457 }, 458 }, 459 { 460 name: "WithMap", 461 tz: time.UTC, 462 uri: "/event/0", 463 events: []*event.NewEvent{ 464 { 465 Published: true, 466 Name: "foo", 467 Location: "id:0", 468 }, 469 }, 470 places: []*place.NewPlace{ 471 { 472 Published: true, 473 Name: "bar", 474 Lat: 1, 475 Lng: 2, 476 }, 477 }, 478 status: 200, 479 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 480 assertMap(t, doc) 481 }, 482 }, 483 { 484 name: "WithoutMap NoLocation", 485 tz: time.UTC, 486 uri: "/event/0", 487 events: []*event.NewEvent{ 488 { 489 Published: true, 490 Name: "foo", 491 }, 492 }, 493 status: 200, 494 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 495 assertNoMap(t, doc) 496 }, 497 }, 498 { 499 name: "WithoutMap NoPlace", 500 tz: time.UTC, 501 uri: "/event/0", 502 events: []*event.NewEvent{ 503 { 504 Published: true, 505 Name: "foo", 506 Location: "bar", 507 }, 508 }, 509 status: 200, 510 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 511 assertNoMap(t, doc) 512 }, 513 }, 514 { 515 name: "WithoutMap NoLatLng", 516 tz: time.UTC, 517 uri: "/event/0", 518 events: []*event.NewEvent{ 519 { 520 Published: true, 521 Name: "foo", 522 Location: "id:0", 523 }, 524 }, 525 places: []*place.NewPlace{ 526 { 527 Published: true, 528 Name: "bar", 529 }, 530 }, 531 status: 200, 532 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 533 assertNoMap(t, doc) 534 }, 535 }, 536 { 537 name: "PastEvents", 538 uri: "/event/0", 539 events: []*event.NewEvent{ 540 { 541 Published: true, 542 Name: "my event", 543 Organizers: []string{"id:0"}, 544 }, 545 // Included 546 { 547 Published: true, 548 Name: "past", 549 Organizers: []string{"id:0"}, 550 Start: time.Now().Add(-1 * time.Hour * 24), 551 }, 552 { 553 Published: true, 554 Name: "multiday", 555 Organizers: []string{"id:0"}, 556 Start: time.Now().Add(-1 * time.Hour * 24), 557 End: tptr(time.Now().Add(-1 * time.Hour * 24 * 4)), 558 }, 559 // Not included 560 { 561 Published: true, 562 Name: "another group", 563 Organizers: []string{"id:1"}, 564 Start: time.Now().Add(-1 * time.Hour * 24), 565 }, 566 { 567 Published: true, 568 Name: "future", 569 Organizers: []string{"id:0"}, 570 Start: time.Now().Add(time.Hour * 24), 571 }, 572 { 573 Published: false, 574 Name: "not published", 575 Organizers: []string{"id:0"}, 576 Start: time.Now().Add(-1 * time.Hour * 24), 577 }, 578 }, 579 groups: []*group.NewGroup{ 580 { 581 Published: true, 582 Name: "group1", 583 }, 584 { 585 Published: true, 586 Name: "group2", 587 }, 588 }, 589 status: 200, 590 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 591 assertPastEvents(t, doc, []string{"past", "multiday"}) 592 }, 593 }, 594 { 595 name: "FutureEvents", 596 uri: "/event/0", 597 events: []*event.NewEvent{ 598 { 599 Published: true, 600 Name: "my event", 601 Organizers: []string{"id:0"}, 602 }, 603 // Included 604 { 605 Published: true, 606 Name: "future", 607 Organizers: []string{"id:0"}, 608 Start: time.Now().Add(time.Hour * 24 * 2), 609 }, 610 { 611 Published: true, 612 Name: "multiday", 613 Organizers: []string{"id:0"}, 614 Start: time.Now().Add(time.Hour * 24 * 2), 615 End: tptr(time.Now().Add(time.Hour * 24 * 4)), 616 }, 617 // Not inlcuded 618 { 619 Published: true, 620 Name: "another group", 621 Organizers: []string{"id:1"}, 622 Start: time.Now().Add(-1 * time.Hour * 24), 623 }, 624 { 625 Published: true, 626 Name: "past", 627 Organizers: []string{"id:0"}, 628 Start: time.Now().Add(-1 * time.Hour * 24), 629 }, 630 { 631 Published: false, 632 Name: "not published", 633 Organizers: []string{"id:0"}, 634 Start: time.Now().Add(time.Hour * 24 * 2), 635 }, 636 }, 637 groups: []*group.NewGroup{ 638 { 639 Published: true, 640 Name: "group1", 641 }, 642 { 643 Published: true, 644 Name: "group2", 645 }, 646 }, 647 status: 200, 648 htmlTestFunc: func(t *testing.T, doc *goquery.Document) { 649 assertFutureEvents(t, doc, []string{"future", "multiday"}) 650 }, 651 }, 652 } 653 654 testRoutes(t, tests) 655 } 656 657 func assertInfoDate(t *testing.T, doc *goquery.Document, date string) { 658 t.Helper() 659 if diff := cmp.Diff(1, len(doc.Find(".info-date").Nodes)); diff != "" { 660 t.Errorf(".info-date mismatch (-want +got):\n%s", diff) 661 } 662 if diff := cmp.Diff(date, removeWhiteSpace(doc.Find(".info-date .info-data").Text())); diff != "" { 663 t.Errorf(".info-date .info-data mismatch (-want +got):\n%s", diff) 664 } 665 } 666 667 func assertInfoOrganizers(t *testing.T, doc *goquery.Document, organizers string) { 668 t.Helper() 669 if organizers == "" { 670 if diff := cmp.Diff(0, len(doc.Find(".info-organizers").Nodes)); diff != "" { 671 t.Errorf(".info-organizers mismatch (-want +got):\n%s", diff) 672 } 673 } else { 674 if diff := cmp.Diff(1, len(doc.Find(".info-organizers").Nodes)); diff != "" { 675 t.Errorf(".info-organizers mismatch (-want +got):\n%s", diff) 676 } 677 if diff := cmp.Diff(organizers, removeWhiteSpace(doc.Find(".info-organizers .info-data").Text())); diff != "" { 678 t.Errorf(".info-organizers .info-data mismatch (-want +got):\n%s", diff) 679 } 680 } 681 } 682 683 func assertInfoLocation(t *testing.T, doc *goquery.Document, location string, location2 string) { 684 t.Helper() 685 if location == "" { 686 if diff := cmp.Diff(1, len(doc.Find(".info-location").Text())); diff != "" { 687 t.Errorf(".info-location mismatch (-want +got):\n%s", diff) 688 } 689 if location2 == "" { 690 if diff := cmp.Diff(location, doc.Find(".info-location .info-data").Text()); diff != "" { 691 t.Errorf(".info-location .info-data mismatch (-want +got):\n%s", diff) 692 } 693 } else { 694 if diff := cmp.Diff(fmt.Sprintf("%s, %s", location, location2), doc.Find(".info-location .info-data").Text()); diff != "" { 695 t.Errorf(".info-location .info-data mismatch (-want +got):\n%s", diff) 696 } 697 } 698 } 699 } 700 701 func removeWhiteSpace(s string) string { 702 noLineBreaks := strings.ReplaceAll(s, "\n", "") 703 noTabs := strings.ReplaceAll(noLineBreaks, " ", "") 704 space := regexp.MustCompile(`\s+`) 705 return strings.TrimSpace(space.ReplaceAllString(noTabs, " ")) 706 } 707 708 func tptr(t time.Time) *time.Time { 709 return &t 710 } 711 712 func TestFormartInterval(t *testing.T) { 713 tests := []struct { 714 interval revent.Interval 715 want string 716 }{ 717 { 718 interval: revent.Interval{ 719 Type: revent.IntervalWeek, 720 Interval: 1, 721 WeekDay: time.Monday, 722 }, 723 want: "Jede Woche Montag", 724 }, 725 } 726 for _, tc := range tests { 727 if formatInterval(tc.interval) != tc.want { 728 t.Errorf("want: %s, got: %s", tc.want, formatInterval(tc.interval)) 729 } 730 } 731 } 732 733 func stringContains(slice []string, item string) bool { 734 set := make(map[string]struct{}, len(slice)) 735 for _, s := range slice { 736 set[s] = struct{}{} 737 } 738 739 _, ok := set[item] 740 return ok 741 }