;; -*- lexical-binding: t; eval: (buttercup-minor-mode) -*-

(load-file "./tests/undercover-init.el")
(require 'subed)
(require 'subed-vtt)
(require 'subed-common)

(defvar mock-vtt-data
  "WEBVTT

00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.

00:03:03.45 --> 00:03:15.5
Baz.
")

(defmacro with-temp-vtt-buffer (&rest body)
  "Call `subed-vtt--init' in temporary buffer before running BODY."
  `(with-temp-buffer
     (subed-vtt-mode)
     (progn ,@body)))

;; (defmacro with-temp-vtt-buffer (&rest body)
;;   "Call `subed-vtt--init' in temporary buffer before running BODY."
;;   `(with-current-buffer (get-buffer-create "*test*")
;;      (erase-buffer)
;;      (unless (derived-mode-p 'subed-vtt-mode) (subed-vtt-mode))
;;      (display-buffer (current-buffer))
;;      (progn ,@body)))

(describe "subed-vtt"
  (describe "Detecting"
    (describe "whether you're in the file header"
      (it "returns t in an empty buffer."
        (with-temp-vtt-buffer
         (expect (subed-in-header-p) :to-be t)))
      (it "works at the beginning of the header."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (expect (subed-in-header-p) :to-be t)))
      (it "works in the middle of the header."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (+ (point-min) 2))
         (expect (subed-in-header-p) :to-be t)))
      (it "returns t on the line before a comment."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
")
         (re-search-backward "\nNOTE")
         (expect (subed-in-header-p) :to-be t)))
      (describe "when the buffer starts with a cue timestamp"
        (it "returns nil from the timing line."
          (with-temp-vtt-buffer
           (insert "00:04:02.234 --> 00:04:10.345
Baz.")
           (goto-char (point-min))
           (expect (subed-in-header-p) :to-be nil)))
        (it "returns nil from the  cue text."
          (with-temp-vtt-buffer
           (insert "00:04:02.234 --> 00:04:10.345
Baz.")
           (expect (subed-in-header-p) :to-be nil))))
      (it "returns nil at the beginning of a comment."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
")
         (re-search-backward "NOTE")
         (expect (subed-in-header-p) :to-be nil)))
      (it "returns nil in the middle of a comment."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
")
         (re-search-backward "comment")
         (expect (subed-in-header-p) :to-be nil)))
      (it "returns nil at the start of an ID."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "^1")
         (expect (subed-in-header-p) :to-be nil)))
      (it "returns nil at the start of a timestamp."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "^0")
         (expect (subed-in-header-p) :to-be nil)))
      (it "returns nil in the middle of timing information."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "--")
         (expect (subed-in-header-p) :to-be nil)))
      (it "returns nil in the middle of a cue."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "This")
         (expect (subed-in-header-p) :to-be nil))
        )
      (it "returns nil in the middle of a cue with the text WEBVTT."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
WEBVTT
")
         (expect (subed-in-header-p) :to-be nil))))
    (describe "whether you're in a comment"
      (it "returns nil in the header."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE This is a test

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (goto-char (point-min))
         (expect (subed-in-comment-p) :to-be nil)))
      (it "returns t at the beginning of a NOTE."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE This is a test

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "NOTE")
         (expect (subed-in-comment-p) :to-be t)))
      (it "returns t in the middle of NOTE."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE This is a test

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "OTE")
         (expect (subed-in-comment-p) :to-be t)))
      (it "returns t in the middle of NOTE text."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE This is a test

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "test")
         (expect (subed-in-comment-p) :to-be t)))
      (it "returns t in the middle of a multi-line NOTE."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "multiple")
         (expect (subed-in-comment-p) :to-be t)))
      (it "returns t in an empty line before an ID."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "\n1")
         (expect (subed-in-comment-p) :to-be t)))
      (it "returns t in an empty line before a timestamp."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "\n0")
         (expect (subed-in-comment-p) :to-be t)))
      (it "returns nil at the beginning of an ID."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "^1")
         (expect (subed-in-comment-p) :to-be nil)))
      (it "returns nil at the beginning of a timestamp."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "^0")
         (expect (subed-in-comment-p) :to-be nil)))
      (it "returns nil in the middle of timing information."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

1
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "--")
         (expect (subed-in-comment-p) :to-be nil)))
      (it "returns t if there's a comment between the cursor and the previous cue."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

1
00:00:00.000 --> 00:00:01.000
This is a subtitle

NOTE
This is a comment
with multiple lines.

2
00:00:00.000 --> 00:00:01.000
This is another subtitle")
         (re-search-backward "multiple")
         (expect (subed-in-comment-p) :to-be t)))
      (it "handles multiple blocks in a cue."
        (with-temp-vtt-buffer
         (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE

This is a comment

2
00:02:02.234 --> 00:02:10.345

Apparently a subtitle can have multiple comements.

Bar.

00:04:02.234 --> 00:04:10.345
Baz.

")
         (re-search-backward "Bar")
         (expect (subed-in-comment-p) :to-be nil)))
      (it "returns nil if there's a cue between the cursor and the previous comment."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

NOTE
This is a comment
with multiple lines.

1
00:00:00.000 --> 00:00:01.000
This is the first subtitle

2
00:00:00.000 --> 00:00:01.000
This is a subtitle
")
         (re-search-backward "first")
         (expect (subed-in-comment-p) :to-be nil)))
      (it "returns nil if there's no comment."
        (with-temp-vtt-buffer
				 (insert "WEBVTT

1
00:00:00.000 --> 00:00:01.000
This is the first subtitle

2
00:00:00.000 --> 00:00:01.000
This is the second subtitle
")
         (re-search-backward "second")
         (expect (subed-in-comment-p) :to-be nil)))))
  (describe "Jumping"
    (describe "to subtitle ID"
      (describe "in the current subtitle"
        (describe "from the header"
          (it "returns nil when the next subtitle starts with a timestamp."
            (with-temp-vtt-buffer
             (insert mock-vtt-data)
             (goto-char (point-min))
             (expect (subed-jump-to-subtitle-id) :to-be nil)))
          (it "returns nil when the next subtitle starts with a comment."
            (with-temp-vtt-buffer
					   (insert "WEBVTT

NOTE

00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.000 --> 00:03:05.123
Bar.

")
             (goto-char (point-min))
             (expect (subed-jump-to-subtitle-id) :to-be nil))
            )
          (it "returns nil when the next subtitle starts with an ID."
            (with-temp-vtt-buffer
					   (insert "WEBVTT

NOTE

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.000 --> 00:03:05.123
Bar.

")
             (goto-char (point-min))
             (expect (subed-jump-to-subtitle-id) :to-be nil))))
        (describe "when there is no comment"
          (it "goes to the ID if specified."
            (with-temp-vtt-buffer
             (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
             (re-search-backward "Foo")
             (expect (subed-jump-to-subtitle-id) :not :to-be nil)
             (expect (looking-at "1") :to-be t)))
          (it "goes to the timestamp if there is no ID."
            (with-temp-vtt-buffer
             (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.

00:04:02.234 --> 00:04:10.345
Baz.

")
             (re-search-backward "Bar")
             (expect (subed-jump-to-subtitle-id) :not :to-be nil)
             (expect (looking-at "00:02:02.234") :to-be t))))
        (describe "when there is no header"
          (it "goes to the timestamp if there is no ID."
            (with-temp-vtt-buffer
             (insert "00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
					   (re-search-backward "Foo")
             (expect (subed-jump-to-subtitle-id) :to-equal 1)))
          )
        (describe "when there is a comment"
          (it "goes to the ID if specified."
            (with-temp-vtt-buffer
             (insert "WEBVTT

NOTE

Hello world

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
             (re-search-backward "Foo")
             (expect (subed-jump-to-subtitle-id) :not :to-be nil)
             (expect (looking-at "1") :to-be t)))
          (it "goes to the timestamp if there is no ID."
            (with-temp-vtt-buffer
             (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE

This is a comment

00:02:02.234 --> 00:02:10.345
Bar.

00:04:02.234 --> 00:04:10.345
Baz.

")
             (re-search-backward "Bar")
             (expect (subed-jump-to-subtitle-id) :not :to-be nil)
             (expect (looking-at "00:02:02.234") :to-be t))))
        (describe "when there are multiple blocks"
          (it "goes to the ID if specified."
            (with-temp-vtt-buffer
             (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE

This is a comment

2
00:02:02.234 --> 00:02:10.345

Apparently a subtitle can have multiple comements.

Bar.

00:04:02.234 --> 00:04:10.345
Baz.

")
             (re-search-backward "Bar")
             (expect (subed-jump-to-subtitle-id) :not :to-be nil)
             (expect (looking-at "2") :to-be t)))
          (it "goes to the timestamp if there is no ID."
            (with-temp-vtt-buffer
             (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE

This is a comment

00:02:02.234 --> 00:02:10.345

Apparently a subtitle can have multiple comements.

Bar.

00:04:02.234 --> 00:04:10.345
Baz.

")
             (re-search-backward "Bar")
             (expect (subed-jump-to-subtitle-id) :not :to-be nil)
             (expect (looking-at "00:02:02.234") :to-be t))))
        (describe "when called from a comment"
          (it "goes to the ID of the subtitle after the comment."
					  (with-temp-vtt-buffer
					   (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
Something goes here

NOTE
This is a comment

2
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
					   (re-search-backward "This is a comment")
					   (expect (subed-jump-to-subtitle-id) :not :to-be nil)
					   (expect (looking-at "2\n") :to-be t)))
          (it "goes to the ID of the subtitle after the comment even at the NOTE line."
					  (with-temp-vtt-buffer
					   (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
Something goes here

NOTE
This is a comment

2
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
					   (re-search-backward "NOTE")
					   (expect (subed-jump-to-subtitle-id) :not :to-be nil)
					   (expect (looking-at "2\n") :to-be t)))
          (it "goes to the timestamp of the subtitle after the comment if no ID is specified."
					  (with-temp-vtt-buffer
					   (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE
This is a comment

00:02:02.234 --> 00:02:10.345
Bar.

00:03:02.234 --> 00:03:10.345
Baz.
")
					   (re-search-backward "This is a comment")
					   (expect (subed-jump-to-subtitle-id) :not :to-be nil)
					   (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)))
          (it "goes to the timestamp of the subtitle after the comment even with a short timestamp."
					  (with-temp-vtt-buffer
					   (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE
This is a comment

02:02.234 --> 00:02:10.345
Bar.

00:03:02.234 --> 00:03:10.345
Baz.
")
					   (re-search-backward "This is a comment")
					   (expect (subed-jump-to-subtitle-id) :not :to-be nil)
					   (expect (looking-at (regexp-quote "02:02.234")) :to-be t)))
          (it "goes to the timestamp of the last subtitle."
					  (with-temp-vtt-buffer
					   (insert mock-vtt-data)
					   (re-search-backward "00:03:03")
					   (expect (subed-jump-to-subtitle-id) :not :to-be nil)
					   (expect (looking-at (regexp-quote "00:03:03")) :to-be t)))))
      (describe "when given an ID"
        (it "returns ID's point if wanted time exists."
          (with-temp-vtt-buffer
           (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

NOTE
This is a comment

00:02:02.234 --> 00:02:10.345
Bar.
")
           (goto-char (point-max))
           (expect (subed-jump-to-subtitle-id "1") :not :to-be nil)
           (expect (looking-at "1\n") :to-be t)))
        (it "returns nil and does not move if wanted ID does not exists."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (goto-char (point-min))
           (search-forward "Foo")
           (let ((stored-point (point)))
             (expect (subed-jump-to-subtitle-id "3") :to-equal nil)
             (expect stored-point :to-equal (point))))))
      (describe "when given a timestamp"
        (it "returns timestamp's point if wanted time exists."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (goto-char (point-max))
           (expect (subed-jump-to-subtitle-id "00:02:02.234") :to-equal 45)
           (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)
           (expect (subed-jump-to-subtitle-id "00:01:01.000") :to-equal 9)
           (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)))
        (it "returns nil and does not move if wanted ID does not exists."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (goto-char (point-min))
           (search-forward "Foo")
           (let ((stored-point (point)))
             (expect (subed-jump-to-subtitle-id "0:08:00") :to-equal nil)
             (expect stored-point :to-equal (point)))))))
    (describe "to subtitle start pos"
			(describe "in the current subtitle"
				(it "returns nil in the header."
					(with-temp-vtt-buffer
					 (insert mock-vtt-data)
					 (goto-char (point-min))
					 (expect (subed-jump-to-subtitle-start-pos) :to-be nil)))
				(it "goes to the ID if specified."
					(with-temp-vtt-buffer
					 (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
					 (re-search-backward "Foo")
					 (expect (subed-jump-to-subtitle-start-pos) :not :to-be nil)
					 (expect (looking-at "1") :to-be t)))
				(it "goes to the timestamp if there is no ID."
					(with-temp-vtt-buffer
					 (insert "WEBVTT

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
					 (re-search-backward "Bar")
					 (expect (subed-jump-to-subtitle-start-pos) :not :to-be nil)
					 (expect (looking-at "00:02:02.234") :to-be t)))
        (it "goes to the comment if there is one."
					(with-temp-vtt-buffer
					 (insert "WEBVTT

NOTE This is a comment

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
					 (re-search-backward "Foo")
					 (expect (subed-jump-to-subtitle-start-pos) :not :to-be nil)
					 (expect (looking-at "NOTE This is a comment") :to-be t)))
				(describe "when called from a comment"
					(it "goes to the start of the comment."
						(with-temp-vtt-buffer
						 (insert "WEBVTT

NOTE
This is a comment

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
						 (re-search-backward "This is a comment")
						 (expect (subed-jump-to-subtitle-start-pos) :not :to-be nil)
						 (expect (looking-at "NOTE\nThis is a comment") :to-be t)))
          (it "goes to the start of the comment from a comment."
						(with-temp-vtt-buffer
						 (insert "WEBVTT

NOTE
This is a comment

1
00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.
")
						 (re-search-backward "OTE")
						 (expect (subed-jump-to-subtitle-start-pos) :not :to-be nil)
						 (expect (looking-at "NOTE\nThis is a comment") :to-be t))))))
    (describe "to subtitle start time"
      (it "returns start time's point if movement was successful."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (expect (subed-jump-to-subtitle-time-start) :to-equal 9)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:01:01.000")
         (re-search-forward "\n\n")
         (expect (subed-jump-to-subtitle-time-start) :to-equal 45)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:02:02.234")
         (re-search-forward "\n\n")
         (expect (subed-jump-to-subtitle-time-start) :to-equal 81)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:03:03.45")))
      (it "returns nil if movement failed."
        (with-temp-vtt-buffer
         (expect (subed-jump-to-subtitle-time-start) :to-equal nil)))
      (describe "when timing info doesn't have a blank line before it"
        :var ((test-data "WEBVTT

00:00:01.000 --> 00:00:01.999
This is a test
00:00:02.000 --> 00:00:02.999
This is another test

NOTE
This is a comment
with a second line.
00:00:03.000 --> 00:00:03.999
This is a third test.
"))
        (it "returns nil from the header."
          (with-temp-vtt-buffer
           (insert test-data)
           (goto-char (point-min))
           (expect (subed-jump-to-subtitle-time-start) :to-be nil)
           (expect (point) :to-equal 1)))
        (it "jumps to the first timing from the start of the timestamp."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "00:00:01\\.000")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:01") :to-be t)))
        (it "jumps to the first timing line from the middle of the timestamp."
          (with-temp-vtt-buffer
           (insert test-data)
           (goto-char (point-min))
           (re-search-forward "--")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:01") :to-be t)))
        (it "jumps to the first timing line from the text."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "This is a test")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:01") :to-be t)))
        (it "jumps to the middle timing line from the start of the timestamp."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "00:00:02\\.000")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:02") :to-be t)))
        (it "jumps to the middle timing line from the middle of the timestamp."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "00:00:02\\.000 --")
           (goto-char (match-end 0))
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:02") :to-be t)))
        (it "jumps to the middle timing line from the text."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "another")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:02") :to-be t)))
        (it "jumps to the last timing line from the start of the comment."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "NOTE")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:03") :to-be t))
          )
        (it "jumps to the last timing line from the middle of the comment."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "comment")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:03") :to-be t)))
        (it "jumps to the last timing line from the end of the comment."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "second line.")
           (goto-char (match-end 0))
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:03") :to-be t)))
        (it "jumps to the last timing line from the start of the timestamp."
          (with-temp-vtt-buffer
           (insert test-data)
           (re-search-backward "00:00:03\\.000")
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:03") :to-be t)))
        (it "jumps to the closest timing line from the end of the file."
          (with-temp-vtt-buffer
           (insert test-data)
           (subed-jump-to-subtitle-time-start)
           (expect (looking-at "00:00:03") :to-be t)))))
    (describe "to subtitle stop time"
      (it "returns stop time's point if movement was successful."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (expect (subed-jump-to-subtitle-time-stop) :to-equal 26)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:01:05.123")
         (re-search-forward "\n\n")
         (expect (subed-jump-to-subtitle-time-stop) :to-equal 62)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:02:10.345")
         (re-search-forward "\n\n")
         (expect (subed-jump-to-subtitle-time-stop) :to-equal 97)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:03:15.5")))
      (it "returns nil if movement failed."
        (with-temp-vtt-buffer
         (expect (subed-jump-to-subtitle-time-stop) :to-equal nil)))
      )
    (describe "to current subtitle timestamp"
      (it "returns timestamp's point when point is already on the timestamp."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-jump-to-subtitle-id "00:01:01.000")
         (expect (subed-jump-to-subtitle-time-start) :to-equal (point))
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:01:01.000")))
      (it "returns timestamp's point when point is on the text."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (search-backward "Baz.")
         (expect (thing-at-point 'word) :to-equal "Baz")
         (expect (subed-jump-to-subtitle-time-start) :to-equal 81)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:03:03.45")))
      (it "returns timestamp's point when point is between subtitles."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (search-forward "Bar.\n")
         (expect (thing-at-point 'line) :to-equal "\n")
         (expect (subed-jump-to-subtitle-time-start) :to-equal 45)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:02:02.234")))
      (it "returns nil if buffer is empty."
        (with-temp-vtt-buffer
         (expect (buffer-string) :to-equal "")
         (expect (subed-jump-to-subtitle-time-start) :to-equal nil)))
      (it "returns timestamp's point when buffer starts with blank lines."
        (with-temp-vtt-buffer
         (insert (concat "WEBVTT \n \t \n" (replace-regexp-in-string "WEBVTT" "" mock-vtt-data)))
         (search-backward "Foo.")
         (expect (thing-at-point 'line) :to-equal "Foo.\n")
         (expect (subed-jump-to-subtitle-time-start) :to-equal 15)
         (expect (looking-at subed--regexp-timestamp) :to-be t)
         (expect (match-string 0) :to-equal "00:01:01.000")))
      ;; I'm not sure this is actually supported by the spec.
      ;; (it "returns timestamp's point when subtitles are separated with blank lines."
      ;;   (with-temp-vtt-buffer
      ;;    (insert mock-vtt-data)
      ;;    (goto-char (point-min))
      ;;    (search-forward "Foo.\n")
      ;;    (insert " \n \t \n")
      ;;    (expect (subed-jump-to-subtitle-time-start) :to-equal 9)
      ;;    (expect (looking-at subed--regexp-timestamp) :to-be t)
      ;;    (expect (match-string 0) :to-equal "00:01:01.000")))
      (it "works with short timestamps from a comment."
        (with-temp-vtt-buffer
         (insert "WEBVTT\n\nNOTE A comment goes here

09:34.900 --> 00:09:37.659
Subtitle 1

00:10:34.900 --> 00:11:37.659
Subtitle 2")
         (re-search-backward "NOTE")
         (goto-char (line-beginning-position))
         (expect (subed-jump-to-subtitle-time-start) :to-equal 35)))

      )

    (describe "to subtitle text"
      (it "returns subtitle text's point if movement was successful."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (expect (subed-jump-to-subtitle-text) :to-equal 39)
         (expect (point) :to-equal (save-excursion (goto-char (point-max)) (search-backward "Foo.")))
         (re-search-forward "\n\n")
         (expect (subed-jump-to-subtitle-text) :to-equal 75)
         (expect (point) :to-equal (save-excursion (goto-char (point-max)) (search-backward "Bar.")))
         (re-search-forward "\n\n")
         (expect (subed-jump-to-subtitle-text) :to-equal 108)
         (expect (point) :to-equal (save-excursion (goto-char (point-max)) (search-backward "Baz.")))))
      (it "returns nil if movement failed."
        (with-temp-vtt-buffer
         (expect (subed-jump-to-subtitle-time-stop) :to-equal nil)))
      (it "works with short timestamps from a comment."
        (with-temp-vtt-buffer
         (insert "WEBVTT\n\nNOTE A comment goes here

09:34.900 --> 00:09:37.659
Subtitle 1

00:10:34.900 --> 00:11:37.659
Subtitle 2")
         (re-search-backward "NOTE")
         (goto-char (line-beginning-position))
         (expect (subed-jump-to-subtitle-text) :to-equal 62)))
      (it "works even when the subtitle has no text and is the only subtitle."
        (with-temp-vtt-buffer
         (insert "00:00:00.000 --> 00:00:01.000

")
         (goto-char (point-min))
         (subed-jump-to-subtitle-text)
         (expect (looking-back "\\.000\n") :to-be t))
        )
      )
    (describe "to end of subtitle text"
      (it "returns point if subtitle end can be found."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (expect (subed-jump-to-subtitle-end) :to-be 43)
         (expect (looking-back "^Foo.$") :to-be t)
         (forward-char 2)
         (expect (subed-jump-to-subtitle-end) :to-be 79)
         (expect (looking-back "^Bar.$") :to-be t)
         (forward-char 2)
         (expect (subed-jump-to-subtitle-end) :to-be 112)
         (expect (looking-back "^Baz.$") :to-be t)
         (goto-char (point-max))
         (backward-char 2)
         (expect (subed-jump-to-subtitle-end) :to-be 112)
         (expect (looking-back "^Baz.$") :to-be t)))
      (it "returns nil if subtitle end cannot be found."
        (with-temp-vtt-buffer
         (expect (subed-jump-to-subtitle-end) :to-be nil)))
      (it "returns nil if point did not move."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (kill-line)
         (expect (subed-jump-to-subtitle-end) :to-be nil)))
      (it "works if text is empty with trailing newline."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (kill-line)
         (backward-char)
         (expect (subed-jump-to-subtitle-end) :to-be 39)
         (expect (looking-at "^$") :to-be t)
         (subed-jump-to-subtitle-text "00:02:02.234")
         (kill-line)
         (backward-char)
         (expect (subed-jump-to-subtitle-end) :to-be 71)
         (expect (looking-at "^$") :to-be t)
         (subed-jump-to-subtitle-text "00:03:03.45")
         (kill-line)
         (backward-char)
         (expect (subed-jump-to-subtitle-end) :to-be 100)
         (expect (looking-at "^$") :to-be t)))
      (it "works if text is empty without trailing newline."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (kill-whole-line)
         (expect (subed-jump-to-subtitle-end) :to-be nil)
         (expect (looking-at "^$") :to-be t)
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (expect (subed-jump-to-subtitle-end) :to-be 39)
         (expect (looking-at "^$") :to-be t)
         (subed-jump-to-subtitle-text "00:02:02.234")
         (kill-whole-line)
         (expect (subed-jump-to-subtitle-end) :to-be nil)
         (expect (looking-at "^$") :to-be t)
         (backward-char)
         (expect (subed-jump-to-subtitle-end) :to-be 70)
         (expect (looking-at "^$") :to-be t)
         (subed-jump-to-subtitle-text "00:03:03.45")
         (kill-whole-line)
         (expect (subed-jump-to-subtitle-end) :to-be nil)
         (expect (looking-at "^$") :to-be t)
         (backward-char)
         (expect (subed-jump-to-subtitle-end) :to-be 98)
         (expect (looking-at "^$") :to-be t)))
      (it "handles linebreaks at the beginning."
        (with-temp-vtt-buffer
         (insert "WEBVTT
Kind: captions
Language: en

00:00:02.459 --> 00:00:05.610 align:start position:0%

Hello world
")
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (subed-jump-to-subtitle-end)
         (expect (point) :to-be-greater-than (- (point-max) 2))))
      (it "works with short timestamps from a comment."
        (with-temp-vtt-buffer
         (insert "WEBVTT\n\nNOTE A comment goes here

09:34.900 --> 00:09:37.659
Subtitle 1

00:10:34.900 --> 00:11:37.659
Subtitle 2")
         (re-search-backward "NOTE")
         (goto-char (line-beginning-position))
         (expect (subed-jump-to-subtitle-end) :to-equal 72)))
      (it "works with optional IDs and multi-line cues where a line is all numbers."
        (with-temp-vtt-buffer
         (insert "WEBVTT

1
00:00:00.000 --> 00:00:01.000
This is first subtitle.
123456789

2
00:00:01.000 --> 00:02:00.000
This is second subtitle.
")
         (re-search-backward "This is first")
         (expect (subed-jump-to-subtitle-end) :to-be 74)))

      (it "works with multiple blocks in a subtitle."
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:00.000 --> 00:00:01.000

A subtitle can consist

of multiple blocks

00:00:01.000 --> 00:02:00.000
This is the second subtitle.
")
         (re-search-backward "A subtitle can")
         (expect (subed-jump-to-subtitle-end) :to-be 82)))
      (it "ignores ending blank lines and spaces."
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:00.000 --> 00:00:01.000

A subtitle can consist

of multiple blocks





00:00:01.000 --> 00:02:00.000
This is the second subtitle.
")
         (re-search-backward "A subtitle can")
         (expect (subed-jump-to-subtitle-end) :to-be 82)))
      (it "ignores ending blank lines at the end of the buffer."
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:00.000 --> 00:00:01.000

A subtitle can consist

of multiple blocks





")
         (re-search-backward "A subtitle can")
         (expect (subed-jump-to-subtitle-end) :to-be 82)))
      (it "stops before lines that have -->."
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a test
a-->
")
         (re-search-backward "This is a test")
         (expect (subed-jump-to-subtitle-end) :to-be 53))))
    (describe "to next subtitle ID"
      (it "returns point when there is a next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:01:01.000")
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-forward-subtitle-id) :to-be 45)
         (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)
         (subed-jump-to-subtitle-time-start "00:02:02.234")
         (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)
         (expect (subed-forward-subtitle-id) :to-be 81)
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)))
      (it "returns nil in an empty buffer."
        (with-temp-vtt-buffer
         (expect (thing-at-point 'word) :to-equal nil)
         (expect (subed-forward-subtitle-id) :to-be nil)))
      (it "moves forward in a buffer."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (expect (thing-at-point 'word) :to-equal "Foo")
         (expect (subed-forward-subtitle-id) :to-be 45)
         (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)
         (subed-jump-to-subtitle-time-stop "00:02:02.234")
         (expect (looking-at (regexp-quote "00:02:10.345")) :to-be t)
         (expect (subed-forward-subtitle-id) :to-be 81)
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)))
      (it "doesn't move when at the last subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:03:03.45")
         (expect (thing-at-point 'word) :to-equal "Baz")
         (expect (subed-forward-subtitle-id) :to-be nil)
         (expect (thing-at-point 'word) :to-equal "Baz")))
      (it "doesn't move when at the last subtitle's time stop."
        (with-temp-vtt-buffer
         (insert (concat mock-vtt-data "\n\n"))
         (subed-jump-to-subtitle-time-stop "00:03:03.45")
         (expect (thing-at-point 'word) :to-equal "00")
         (expect (subed-forward-subtitle-id) :to-be nil)
         (expect (thing-at-point 'word) :to-equal "00")))
      (it "finds the next subtitle timing even when there's no blank line before it."
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:01.000 --> 00:00:01.999
This is a test
00:00:02.000 --> 00:00:02.999
This is another test

NOTE
This is a comment
with a second line.
00:00:03.000 --> 00:00:03.999
This is a third test.
")
         (re-search-backward "This is a test")
         (subed-forward-subtitle-id)
         (expect (looking-at "00:00:02") :to-be t)
         )))
    (describe "to previous subtitle ID"
      (it "returns point when there is a previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:02:02.234")
         (expect (thing-at-point 'word) :to-equal "Bar")
         (expect (subed-backward-subtitle-id) :to-be 9)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (subed-jump-to-subtitle-time-stop "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:15.5")) :to-be t)
         (expect (subed-backward-subtitle-id) :to-be 45)
         (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)))
      (it "does not get confused by empty lines at the end of the buffer."
        (with-temp-vtt-buffer
         (insert mock-vtt-data "\n\n")
         (expect (subed-backward-subtitle-id) :not :to-be nil)))
      (it "returns nil and doesn't move when there is no previous subtitle."
        (with-temp-vtt-buffer
         (expect (subed-backward-subtitle-id) :to-be nil))
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:01:01.000")
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-backward-subtitle-id) :to-be nil)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t))
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (expect (thing-at-point 'word) :to-equal "Foo")
         (expect (subed-backward-subtitle-id) :to-be nil)
         (expect (thing-at-point 'word) :to-equal "Foo"))
        (with-temp-vtt-buffer
         (insert (concat "\n\n\n" mock-vtt-data))
         (subed-jump-to-subtitle-time-stop "00:01:01.000")
         (expect (thing-at-point 'word) :to-equal "00")
         (expect (subed-backward-subtitle-id) :to-be nil)
         (expect (thing-at-point 'word) :to-equal "00")))
      )
    (describe "to next subtitle text"
      (it "returns point when there is a next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:01:01.000")
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-forward-subtitle-text) :to-be 75)
         (expect (thing-at-point 'word) :to-equal "Bar")))
      (it "returns nil and doesn't move when there is no next subtitle."
        (with-temp-vtt-buffer
         (goto-char (point-max))
         (insert (concat mock-vtt-data "\n\n"))
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)
         (expect (subed-forward-subtitle-text) :to-be nil)
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)))
      (it "handles blank lines at the start of a caption."
        (with-temp-vtt-buffer
         (insert "WEBVTT
Kind: captions
Language: en

00:00:02.459 --> 00:00:05.610 align:start position:0%

hi<00:00:03.459><c> welcome</c><00:00:03.850><c> to</c><00:00:03.999><c> another</c><00:00:04.149><c> episode</c><00:00:04.509><c> of</c><00:00:05.020><c> Emacs</c>
")
         (goto-char (point-min))
         (subed-forward-subtitle-text)
         (expect (looking-at "\nhi") :to-be t)))
      )
    (describe "to previous subtitle text"
      (it "returns point when there is a previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)
         (expect (subed-backward-subtitle-text) :to-be 75)
         (expect (thing-at-point 'word) :to-equal "Bar")))
      (it "returns nil and doesn't move when there is no previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-forward-subtitle-time-start)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-backward-subtitle-text) :to-be nil)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)))
      )
    (describe "to next subtitle end"
      (it "returns point when there is a next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:02:02.234")
         (expect (thing-at-point 'word) :to-equal "Bar")
         (expect (subed-forward-subtitle-end) :to-be 112)
         (expect (thing-at-point 'word) :to-equal nil)))
      (it "returns nil and doesn't move when there is no next subtitle."
        (with-temp-vtt-buffer
         (insert (concat mock-vtt-data "\n\n"))
         (subed-jump-to-subtitle-text "00:03:03.45")
         (end-of-line)
         (expect (thing-at-point 'word) :to-equal nil)
         (expect (subed-forward-subtitle-end) :to-be nil)
         (expect (thing-at-point 'word) :to-equal nil)))
      )
    (describe "to previous subtitle end"
      (it "returns point when there is a previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)
         (expect (subed-backward-subtitle-text) :to-be 75)
         (expect (thing-at-point 'word) :to-equal "Bar")))
      (it "returns nil and doesn't move when there is no previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-forward-subtitle-id)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-backward-subtitle-text) :to-be nil)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)))
      )
    (describe "to next subtitle start time"
      (it "returns point when there is a next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (expect (thing-at-point 'word) :to-equal "Foo")
         (expect (subed-forward-subtitle-time-start) :to-be 45)
         (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)))
      (it "returns nil and doesn't move when there is no next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)
         (expect (subed-forward-subtitle-time-start) :to-be nil)
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)))
      )
    (describe "to previous subtitle start time"
      (it "returns point when there is a previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:02:02.234")
         (expect (looking-at (regexp-quote "00:02:02.234")) :to-be t)
         (expect (subed-backward-subtitle-time-start) :to-be 9)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)))
      (it "returns nil and doesn't move when there is no previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:01:01.000")
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-backward-subtitle-time-start) :to-be nil)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)))
      )
    (describe "to next subtitle stop time"
      (it "returns point when there is a next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-text "00:01:01.000")
         (expect (thing-at-point 'word) :to-equal "Foo")
         (expect (subed-forward-subtitle-time-stop) :to-be 62)
         (expect (looking-at (regexp-quote "00:02:10.345")) :to-be t)))
      (it "returns nil and doesn't move when there is no next subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)
         (expect (subed-forward-subtitle-time-stop) :to-be nil)
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)))
      )
    (describe "to previous subtitle stop time"
      (it "returns point when there is a previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (looking-at (regexp-quote "00:03:03.45")) :to-be t)
         (expect (subed-backward-subtitle-time-stop) :to-be 62)
         (expect (looking-at (regexp-quote "00:02:10.345")) :to-be t)))
      (it "returns nil and doesn't move when there is no previous subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:01:01.000")
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)
         (expect (subed-backward-subtitle-time-stop) :to-be nil)
         (expect (looking-at (regexp-quote "00:01:01.000")) :to-be t)))
      ))
  (describe "Getting"
    (describe "the subtitle ID"
      (it "returns the subtitle ID if it can be found."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (re-search-backward "00:01:01.000")
         (expect (subed-subtitle-id) :to-equal "00:01:01.000")))
      (it "returns nil if no subtitle ID can be found."
        (with-temp-vtt-buffer
         (expect (subed-subtitle-id) :to-equal nil)))
      (it "handles extra attributes"
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:01.000 --> 00:00:02.000 align:start position:0%
Hello world")
         (expect (subed-subtitle-id) :to-equal "00:00:01.000"))))
    (describe "the subtitle ID at playback time"
      (it "returns subtitle ID if time is equal to start time."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (expect (subed-subtitle-id-at-msecs (subed-timestamp-to-msecs "00:01:01.000"))
                 :to-equal "00:01:01.000")))
      (it "returns subtitle ID if time is equal to stop time."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (expect (subed-subtitle-id-at-msecs (subed-timestamp-to-msecs "00:02:10.345"))
                 :to-equal "00:02:02.234")))
      (it "returns subtitle ID if time is between start and stop time."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (expect (subed-subtitle-id-at-msecs (subed-timestamp-to-msecs "00:02:05.345"))
                 :to-equal "00:02:02.234")))
      (it "returns nil if time is before the first subtitle's start time."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (let ((msecs (- (save-excursion
                           (goto-char (point-min))
                           (subed-forward-subtitle-id)
                           (subed-subtitle-msecs-start))
                         1)))
           (expect (subed-subtitle-id-at-msecs msecs) :to-equal nil))))
      (it "returns nil if time is after the last subtitle's start time."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (let ((msecs (+ (save-excursion
                           (goto-char (point-max))
                           (subed-subtitle-msecs-stop)) 1)))
           (expect (subed-subtitle-id-at-msecs msecs) :to-equal nil))))
      (it "returns nil if time is between subtitles."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (expect (subed-subtitle-id-at-msecs (subed-timestamp-to-msecs "00:01:06.123"))
                 :to-equal nil))))
    (describe "the subtitle start/stop time"
      (it "returns the time in milliseconds."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:02:02.234")
         (expect (subed-subtitle-msecs-start) :to-equal (+ (* 2 60000) (* 2 1000) 234))
         (expect (subed-subtitle-msecs-stop) :to-equal (+ (* 2 60000) (* 10 1000) 345))))
      (it "handles lack of digits in milliseconds gracefully."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (expect (save-excursion (subed-jump-to-subtitle-time-start)
                                 (thing-at-point 'line)) :to-equal "00:03:03.45 --> 00:03:15.5\n")
         (expect (subed-subtitle-msecs-start) :to-equal (+ (* 3 60 1000) (*  3 1000) 450))
         (expect (subed-subtitle-msecs-stop)  :to-equal (+ (* 3 60 1000) (* 15 1000) 500))))
      (it "handles lack of hours in milliseconds gracefully."
        (with-temp-vtt-buffer
         (insert "WEBVTT\n\n01:02.000 --> 03:04.000\nHello\n")
         (expect (subed-subtitle-msecs-start) :to-equal (+ (* 1 60 1000) (* 2 1000)))
         (expect (subed-subtitle-msecs-stop) :to-equal (+ (* 3 60 1000) (* 4 1000)))))
      (it "returns nil if time can't be found."
        (with-temp-vtt-buffer
         (expect (subed-subtitle-msecs-start) :to-be nil)
         (expect (subed-subtitle-msecs-stop) :to-be nil)))
      )
    (describe "the subtitle text"
      (describe "when text is empty"
        (it "and at the beginning with a trailing newline."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:01:01.000")
           (kill-line)
           (expect (subed-subtitle-text) :to-equal "")))
        (it "and at the beginning without a trailing newline."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:01:01.000")
           (kill-whole-line)
           (expect (subed-subtitle-text) :to-equal "")))
        (it "and in the middle."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:02:02.234")
           (kill-line)
           (expect (subed-subtitle-text) :to-equal "")))
        (it "and at the end with a trailing newline."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:03:03.45")
           (kill-line)
           (expect (subed-subtitle-text) :to-equal "")))
        (it "and at the end without a trailing newline."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:03:03.45")
           (kill-whole-line)
           (expect (subed-subtitle-text) :to-equal "")))
        )
      (describe "when text is not empty"
        (it "handles no linebreaks."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:02:02.234")
           (expect (subed-subtitle-text) :to-equal "Bar.")))
        (it "handles linebreaks."
          (with-temp-vtt-buffer
           (insert mock-vtt-data)
           (subed-jump-to-subtitle-text "00:02:02.234")
           (insert "Bar.\n")
           (expect (subed-subtitle-text) :to-equal "Bar.\nBar.")))
        (it "handles linebreaks at the beginning."
          (with-temp-vtt-buffer
           (insert "WEBVTT
Kind: captions
Language: en

00:00:02.459 --> 00:00:05.610 align:start position:0%

Hello world
")
           (subed-jump-to-subtitle-text "00:00:02.459")
           (expect (subed-subtitle-text) :to-equal "\nHello world")))
        )
      )
    (describe "the point within the subtitle"
      (it "returns the relative point if we can find an ID."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:02:02.234")
         (expect (subed-subtitle-relative-point) :to-equal 0)
         (forward-line)
         (expect (subed-subtitle-relative-point) :to-equal 30)
         (forward-char)
         (expect (subed-subtitle-relative-point) :to-equal 31)
         (forward-line)
         (expect (subed-subtitle-relative-point) :to-equal 35)
         (forward-line)
         (expect (subed-subtitle-relative-point) :to-equal 0)))
      (it "returns nil if we can't find an ID."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:01:01.000")
         (insert "foo")
         (expect (subed-subtitle-relative-point) :to-equal nil)))
      )
    (describe "the subtitle start position"
      (it "returns the start from inside a subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (re-search-backward "Bar")
         (expect (subed-subtitle-start-pos) :to-equal 45)))
      (it "returns the start from the beginning of the line."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (re-search-backward "00:02:02\\.234")
         (expect (subed-subtitle-start-pos) :to-equal 45)))
      (it "returns the start of a comment"
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (re-search-backward "00:02:02\\.234")
         (insert "NOTE\n\nThis is a comment\n\n")
         (expect (subed-subtitle-start-pos) :to-equal 45)))))
  (describe "Converting to msecs"
    (it "works with numbers."
      (expect (with-temp-vtt-buffer (subed-to-msecs 5123)) :to-equal 5123))
    (it "works with numbers as strings."
      (expect (with-temp-vtt-buffer (subed-to-msecs "5123")) :to-equal 5123))
    (it "works with timestamps."
      (expect (with-temp-vtt-buffer
               (subed-to-msecs "00:00:05.124")) :to-equal 5124)))

  (describe "Setting start/stop time"
    (it "of current subtitle updates it."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-end "00:02:02.234")
       (subed-set-subtitle-time-start (+ (* 1 60 60 1000) (* 2 60 1000) (* 3 1000) 400) nil t t)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "01:02:03.400 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))
       (subed-set-subtitle-time-stop (+ (* 5 60 60 1000) (* 6 60 1000) (* 7 1000) 800) nil t t)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "01:02:03.400 --> 05:06:07.800\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))))
    (it "of specific subtitle updates it."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-time-stop "00:01:01.000")
       (subed-set-subtitle-time-start (+ (* 2 60 60 1000) (* 4 60 1000) (* 6 1000) 800) 1 t t)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "02:04:06.800 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))
       (subed-jump-to-subtitle-text "00:03:03.45")
       (subed-set-subtitle-time-stop (+ (* 3 60 60 1000) (* 5 60 1000) (* 7 1000) 900) 3 t t)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "02:04:06.800 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 03:05:07.900\n"
                                                 "Baz.\n"))))
    (it "when milliseconds lack digits, fills the rest in."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-id "00:03:03.45")
       (subed-set-subtitle-time-start (+ (* 1 60 60 1000) (* 2 60 1000) (* 3 1000) 4) 3 t t)
       (expect (save-excursion (subed-jump-to-subtitle-time-start)
                               (thing-at-point 'line))
               :to-equal "01:02:03.004 --> 00:03:15.5\n")
       (subed-set-subtitle-time-stop (+ (* 2 60 60 1000) (* 3 60 1000) (* 4 1000) 60) 3 t t)
       (expect (save-excursion (subed-jump-to-subtitle-time-start)
                               (thing-at-point 'line))
               :to-equal "01:02:03.004 --> 02:03:04.060\n"))))
  (describe "Shifting subtitles"
    (describe "starting at a specific timestamp"
      (it "works when called from the start of the buffer."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-shift-subtitles-to-start-at-timestamp 500)
         (let ((data (subed-subtitle-list)))
           (expect (elt (elt (subed-subtitle-list) 0) 1) :to-equal 500)
           (expect (elt (elt (subed-subtitle-list) 0) 2) :to-equal 4623)
           (expect (elt (elt (subed-subtitle-list) 2) 1) :to-equal 122950))))
      (it "only affects the current and following subtitles."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (re-search-backward "^Bar")
         (subed-shift-subtitles-to-start-at-timestamp 120000)
         (let ((data (subed-subtitle-list)))
           (expect (elt (elt (subed-subtitle-list) 0) 1) :to-equal 61000)
           (expect (elt (elt (subed-subtitle-list) 1) 1) :to-equal 120000))))))

  (describe "Inserting a subtitle"
    (describe "in an empty buffer"
      (describe "before"
        (it "creates a cue with default values."
          (with-temp-vtt-buffer
           (expect (subed-prepend-subtitle) :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:00:00.000 --> 00:00:01.000\n\n"))
           (expect (point) :to-equal 31)))
        (it "creates a cue with a start time."
          (with-temp-vtt-buffer
           (expect (subed-prepend-subtitle nil 60000) :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:01:00.000 --> 00:01:01.000\n\n"))
           (expect (point) :to-equal 31)))
        (it "creates a cue with a start time and stop time."
          (with-temp-vtt-buffer
           (expect (subed-prepend-subtitle nil 60000 65000) :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:01:00.000 --> 00:01:05.000\n\n"))
           (expect (point) :to-equal 31)))
        (it "creates a cue with a start time, stop time and text."
          (with-temp-vtt-buffer
           (expect (subed-prepend-subtitle nil 60000 65000 "Foo. bar\nbaz.") :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:01:00.000 --> 00:01:05.000\n"
                                                     "Foo. bar\nbaz.\n"))
           (expect (point) :to-equal 31)))
        )
      (describe "when appending"
        (it "creates a subtitle with default arguments."
          (with-temp-vtt-buffer
           (expect (subed-append-subtitle) :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:00:00.000 --> 00:00:01.000\n\n"))
           (expect (point) :to-equal 31)))
        (it "creates a subtitle with a start time."
          (with-temp-vtt-buffer
           (expect (subed-append-subtitle nil 60000) :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:01:00.000 --> 00:01:01.000\n\n"))
           (expect (point) :to-equal 31)))
        (it "creates a subtitle with a start time and stop time."
          (with-temp-vtt-buffer
           (expect (subed-append-subtitle nil 60000 65000) :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:01:00.000 --> 00:01:05.000\n\n"))
           (expect (point) :to-equal 31)))
        (it "creates a subtitle with a start time, stop time and text."
          (with-temp-vtt-buffer
           (expect (subed-append-subtitle nil 60000 65000 "Foo, bar\nbaz.") :to-equal 31)
           (expect (buffer-string) :to-equal (concat "00:01:00.000 --> 00:01:05.000\n"
                                                     "Foo, bar\nbaz.\n"))
           (expect (point) :to-equal 31)))
        (it "creates a subtitle with a start time, stop time, text, and a single-line comment."
          (with-temp-vtt-buffer
           (subed-append-subtitle nil 60000 65000 "Foo, bar\nbaz." "Hello")
           (expect (buffer-string) :to-equal (concat "NOTE Hello\n\n00:01:00.000 --> 00:01:05.000\n"
                                                     "Foo, bar\nbaz.\n"))
           (expect (looking-at "Foo") :to-be t)))
        (it "creates a subtitle with a start time, stop time, text, and a multi-line comment."
          (with-temp-vtt-buffer
           (subed-append-subtitle nil 60000 65000 "Foo, bar\nbaz." "Hello\nworld")
           (expect (buffer-string) :to-equal (concat "NOTE\nHello\nworld\n\n00:01:00.000 --> 00:01:05.000\n"
                                                     "Foo, bar\nbaz.\n"))
           (expect (looking-at "Foo") :to-be t)))))
    (describe "in a non-empty buffer"
      (describe "before the current subtitle"
        (describe "with point on the first subtitle"
          (it "passing nothing."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-time-stop)
             (expect (subed-prepend-subtitle) :to-equal 31)
             (expect (buffer-string) :to-equal (concat "00:00:00.000 --> 00:00:01.000\n"
                                                       "\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n"))
             (expect (point) :to-equal 31)))
          (it "passing start time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-time-stop)
             (expect (subed-prepend-subtitle nil 1500) :to-equal 31)
             (expect (buffer-string) :to-equal (concat "00:00:01.500 --> 00:00:02.500\n"
                                                       "\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n"))
             (expect (point) :to-equal 31)))
          (it "passing start time and stop time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-time-stop)
             (expect (subed-prepend-subtitle nil 1500 2000) :to-equal 31)
             (expect (buffer-string) :to-equal (concat "00:00:01.500 --> 00:00:02.000\n"
                                                       "\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n"))
             (expect (point) :to-equal 31)))
          (it "passing start time, stop time and text."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-time-stop)
             (expect (subed-prepend-subtitle nil 1500 3000 "Bar.") :to-equal 31)
             (expect (buffer-string) :to-equal (concat "00:00:01.500 --> 00:00:03.000\n"
                                                       "Bar.\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n"))
             (expect (point) :to-equal 31)))
          )
        (describe "with point on a non-first subtitle"
          (it "passing nothing."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n\n"
                             "00:00:10.000 --> 00:00:12.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-text "00:00:10.000")
             (expect (subed-prepend-subtitle) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:00.000 --> 00:00:01.000\n"
                                                       "\n\n"
                                                       "00:00:10.000 --> 00:00:12.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n\n"
                             "00:00:10.000 --> 00:00:12.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-text "00:00:10.000")
             (expect (subed-prepend-subtitle nil 7000) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:07.000 --> 00:00:08.000\n"
                                                       "\n\n"
                                                       "00:00:10.000 --> 00:00:12.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time and stop time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n\n"
                             "00:00:10.000 --> 00:00:12.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-text "00:00:10.000")
             (expect (subed-prepend-subtitle nil 7000 7123) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:07.000 --> 00:00:07.123\n"
                                                       "\n\n"
                                                       "00:00:10.000 --> 00:00:12.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time, stop time and text."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n\n"
                             "00:00:10.000 --> 00:00:12.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-text "00:00:10.000")
             (expect (subed-prepend-subtitle nil 7000 7123 "Baz.") :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:07.000 --> 00:00:07.123\n"
                                                       "Baz.\n\n"
                                                       "00:00:10.000 --> 00:00:12.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          )
        )
      (describe "after the current subtitle"
        (describe "with point on the last subtitle"
          (it "passing nothing."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-text)
             (expect (subed-append-subtitle) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:00.000 --> 00:00:01.000\n"
                                                       "\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-text)
             (expect (subed-append-subtitle nil 12345) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:12.345 --> 00:00:13.345\n"
                                                       "\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time and stop time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-text)
             (expect (subed-append-subtitle nil 12345 15000) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:12.345 --> 00:00:15.000\n"
                                                       "\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time, stop time and text."
            (with-temp-vtt-buffer
             (insert (concat "00:00:05.000 --> 00:00:06.000\n"
                             "Foo.\n"))
             (subed-jump-to-subtitle-text)
             (expect (subed-append-subtitle nil 12345 15000 "Bar.") :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:05.000 --> 00:00:06.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:12.345 --> 00:00:15.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          )
        (describe "with point on a non-last subtitle"
          (it "inserts an empty subtitle."
            (with-temp-vtt-buffer
             (insert (concat "00:00:01.000 --> 00:00:02.000\n"
                             "Foo.\n\n"
                             "00:00:05.000 --> 00:00:06.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-time-start "00:00:01.000")
             (subed-append-subtitle)
             (expect (point) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:01.000 --> 00:00:02.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:00.000 --> 00:00:01.000\n"
                                                       "\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:01.000 --> 00:00:02.000\n"
                             "Foo.\n\n"
                             "00:00:05.000 --> 00:00:06.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-time-start "00:00:01.000")
             (expect (subed-append-subtitle nil 2500) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:01.000 --> 00:00:02.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:02.500 --> 00:00:03.500\n"
                                                       "\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time and stop time."
            (with-temp-vtt-buffer
             (insert (concat "00:00:01.000 --> 00:00:02.000\n"
                             "Foo.\n\n"
                             "00:00:05.000 --> 00:00:06.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-time-start "00:00:01.000")
             (expect (subed-append-subtitle nil 2500 4000) :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:01.000 --> 00:00:02.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:02.500 --> 00:00:04.000\n"
                                                       "\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          (it "passing start time, stop time and text."
            (with-temp-vtt-buffer
             (insert (concat "00:00:01.000 --> 00:00:02.000\n"
                             "Foo.\n\n"
                             "00:00:05.000 --> 00:00:06.000\n"
                             "Bar.\n"))
             (subed-jump-to-subtitle-time-start "00:00:01.000")
             (expect (subed-append-subtitle nil 2500 4000 "Baz.") :to-equal 67)
             (expect (buffer-string) :to-equal (concat "00:00:01.000 --> 00:00:02.000\n"
                                                       "Foo.\n\n"
                                                       "00:00:02.500 --> 00:00:04.000\n"
                                                       "Baz.\n\n"
                                                       "00:00:05.000 --> 00:00:06.000\n"
                                                       "Bar.\n"))
             (expect (point) :to-equal 67)))
          )
        )
      (describe "before a comment"
        (it "inserts before the comment."
          (with-temp-vtt-buffer
           (insert (concat "00:00:01.000 --> 00:00:02.000\n"
                           "Foo.\n\n"
                           "NOTE comment\n\n00:00:05.000 --> 00:00:06.000\n"
                           "Bar.\n"))
           (subed-jump-to-subtitle-time-start "00:00:01.000")
           (expect (subed-append-subtitle nil 2500 4000 "Baz.") :to-equal 67)
           (expect (buffer-string) :to-equal (concat "00:00:01.000 --> 00:00:02.000\n"
                                                     "Foo.\n\n"
                                                     "00:00:02.500 --> 00:00:04.000\n"
                                                     "Baz.\n\n"
                                                     "NOTE comment\n\n00:00:05.000 --> 00:00:06.000\n"
                                                     "Bar.\n"))
           (expect (point) :to-equal 67))
          )
        )
      (it "when point is on empty text."
        (with-temp-vtt-buffer
         (insert (concat "00:00:01.000 --> 00:00:02.000\n"
                         "\n"))
         (forward-char -1)
         (subed-jump-to-subtitle-text)
         (expect (subed-append-subtitle) :to-equal 63)
         (expect (buffer-string) :to-equal (concat "00:00:01.000 --> 00:00:02.000\n"
                                                   "\n\n"
                                                   "00:00:00.000 --> 00:00:01.000\n"
                                                   "\n"))
         (expect (point) :to-equal 63)))
      )
    )

  (describe "Killing a subtitle"
    (it "removes the first subtitle."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:01:01.000")
       (subed-kill-subtitle)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))))
    (it "removes it in between."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:02:02.234")
       (subed-kill-subtitle)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))))
    (it "removes the last subtitle."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:03:03.45")
       (subed-kill-subtitle)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n"))))
    (describe "removes the previous subtitle when point is right above the timestamp"
      (it "of the last subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:03:03.45")
         (backward-char)
         (expect (looking-at "^\n00:03:03.45") :to-be t)
         (subed-kill-subtitle)
         (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                   "00:01:01.000 --> 00:01:05.123\n"
                                                   "Foo.\n\n"
                                                   "00:03:03.45 --> 00:03:15.5\n"
                                                   "Baz.\n"))))
      (it "of a non-last subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (subed-jump-to-subtitle-id "00:02:02.234")
         (backward-char)
         (expect (looking-at "^\n00:02:02.234") :to-be t)
         (subed-kill-subtitle)
         (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                   "00:02:02.234 --> 00:02:10.345\n"
                                                   "Bar.\n\n"
                                                   "00:03:03.45 --> 00:03:15.5\n"
                                                   "Baz.\n"))))
      )
    )

  (describe "Validating"
    (it "works in empty buffer."
      (with-temp-vtt-buffer
       (subed-validate)))
    (it "works in buffer that contains only newlines."
      (with-temp-vtt-buffer
       (cl-loop for _ from 1 to 10 do
                (insert "\n")
                (subed-validate))))
    (it "works in buffer that contains only spaces."
      (with-temp-vtt-buffer
       (cl-loop for _ from 1 to 10 do
                (insert " ")
                (subed-validate))))
    (it "works in buffer that contains only spaces and newlines."
      (with-temp-vtt-buffer
       (cl-loop for _ from 1 to 10 do
                (if (eq (random 2) 0)
                    (insert " ")
                  (insert "\n"))
                (subed-validate))))
    (it "reports invalid stop time."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-time-stop "00:01:01.000")
       (forward-char 10)
       (insert "3")
       (expect (subed-validate) :to-throw
               'error '("Found invalid stop time: \"00:01:01.000 --> 00:01:05.1323\""))
       (expect (point) :to-equal 26)))
    (it "runs before saving."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-time-stop "00:01:01.000")
       (forward-char 10)
       (insert "3")
       (expect (subed-prepare-to-save) :to-throw
               'error '("Found invalid stop time: \"00:01:01.000 --> 00:01:05.1323\""))
       (expect (point) :to-equal 26)))
    (it "reports invalid time separator."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-time-stop "00:01:01.000")
       (delete-char -1)
       (expect (subed-validate) :to-throw
               'error '("Found invalid separator between start and stop time: \"00:01:01.000 -->00:01:05.123\""))
       (expect (point) :to-equal 21)))
    (it "does not report error when last subtitle text is empty."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:03:03.45")
       (kill-whole-line)
       (forward-char -2)
       (subed-validate)
       (expect (point) :to-equal 106)))
    (it "accepts mm:ss timestamps."
      (with-temp-vtt-buffer
       (insert "WebVTT\n\n00:00.003 --> 00:05.123\nThis is a test")
       (subed-validate)
       (expect (point) :to-equal (point-max))))
    (it "preserves point if there is no error."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:02:02.234")
       (forward-char 2)
       (subed-validate)
       (expect (point) :to-equal 77)))
    (it "accepts cue text that starts with something that looks like a timestamp."
      (with-temp-vtt-buffer
       (insert "WebVTT\n\n00:00:00.003 --> 00:00:05.123\n12:00 is noon.\n\n00:10:00.003 --> 00:11:05.123\nThis should be fine.")
       (subed-validate)
       (expect (point) :to-equal (point-max))))
    (it "signals an error when timing lines are not preceded by blank lines or IDs."
      (with-temp-vtt-buffer
       (insert "WEBVTT

-->
00:00:00.000 --> 00:00:01.000
text0
foo-->
00:00:00.000 --> 00:00:01.000
text1
-->foo
00:00:00.000 --> 00:00:01.000
text2
--->
00:00:00.000 --> 00:00:01.000
text3
-->-->
00:00:00.000 --> 00:00:01.000
text4
00:00:00.000 --> 00:00:01.000
text5

00:00:00.000 -a -->

00:00:00.000 --a -->

00:00:00.000 - -->

00:00:00.000 -- -->
")
       (expect (subed-validate) :to-throw
               'error '("Found timing line without blank line before it: 00:00:00.000 --> 00:00:01.000"))
       (expect (point) :to-equal 13)))
    (it "is fine if there's a blank line before an ID and a timing line."
      (with-temp-vtt-buffer
       (insert "WEBVTT

00:00:00.000 --> 00:00:01.000
text1

5
00:00:00.000 --> 00:00:01.000
Test
")
       (expect (subed-validate) :to-be nil))))
  (describe "Sanitizing"
    (it "removes trailing tabs and spaces from all lines."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (while (re-search-forward "\n" nil t)
         (replace-match " \n"))
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data))
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (while (re-search-forward "\n" nil t)
         (replace-match "\t\n"))
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "removes leading tabs and spaces from all lines."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (while (re-search-forward "\n" nil t)
         (replace-match "\n "))
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data))
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (while (re-search-forward "\n" nil t)
         (replace-match "\n\t"))
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "removes excessive empty lines between subtitles."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (forward-line 2)
       (while (re-search-forward "\n\n" nil t)
         (replace-match "\n\n\n\n\n\n"))
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "retains comments."
      (with-temp-vtt-buffer
       (insert (concat "WEBVTT\n\n"
                       "00:01:01.000 --> 00:01:05.123\n"
                       "Foo.\n\nNOTE This is a test\n\n"
                       "00:02:02.234 --> 00:02:10.345\n"
                       "Bar.\n\n"
                       "NOTE\nAnother comment\n\n"
                       "00:03:03.45 --> 00:03:15.5\n"
                       "Baz.\n"))
       (subed-sanitize)
       (expect (buffer-string) :to-match "NOTE This is a test")
       (expect (buffer-string) :to-match "Another comment")))
    (it "ensures double newline between subtitles if text of previous subtitle is empty."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:01:01.000")
       (kill-whole-line)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))
       (subed-sanitize)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))))
    (it "ensures single newline before timing lines."
      (with-temp-vtt-buffer
       (insert (concat "WEBVTT\n\n"
                       "00:01:01.000 --> 00:01:05.123\n"
                       "Foo.\n"
                       "00:02:02.234 --> 00:02:10.345\n"
                       "Bar.\n\n\n"
                       "00:03:03.45 --> 00:03:15.5\n"
                       "Baz.\n"))
       (subed-sanitize)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "Baz.\n"))))

    (it "removes empty lines from end of buffer."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-max))
       (insert " \n\n\n")
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "ensures a single newline after the last subtitle."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-max))
       (while (eq (char-before (point-max)) ?\n)
         (delete-backward-char 1))
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "ensures single newline after last subtitle if text is empty."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:03:03.45")
       (kill-whole-line)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 ""))
       (subed-sanitize)
       (expect (buffer-string) :to-equal (concat "WEBVTT\n\n"
                                                 "00:01:01.000 --> 00:01:05.123\n"
                                                 "Foo.\n\n"
                                                 "00:02:02.234 --> 00:02:10.345\n"
                                                 "Bar.\n\n"
                                                 "00:03:03.45 --> 00:03:15.5\n"
                                                 "\n"))))
    (it "ensures single space before and after time separators."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (re-search-forward " --> ")
       (replace-match "  --> ")
       (re-search-forward " --> ")
       (replace-match " -->  ")
       (re-search-forward " --> ")
       (replace-match "-->")
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-sanitize)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "runs before saving."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (re-search-forward " --> ")
       (replace-match "  --> ")
       (re-search-forward " --> ")
       (replace-match " -->  ")
       (re-search-forward " --> ")
       (replace-match "-->")
       (expect (buffer-string) :not :to-equal mock-vtt-data)
       (subed-prepare-to-save)
       (expect (buffer-string) :to-equal mock-vtt-data)))
    (it "does not insert newline in empty buffer."
      (with-temp-vtt-buffer
       (expect (buffer-string) :to-equal "")
       (subed-sanitize)
       (expect (buffer-string) :to-equal "")))
    )


  (describe "Sorting"
    (it "orders subtitles by start time."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (goto-char (point-min))
       (re-search-forward "01:01")
       (replace-match "12:01")
       (goto-char (point-min))
       (re-search-forward "02:02")
       (replace-match "10:02")
       (goto-char (point-min))
       (re-search-forward "03:03")
       (replace-match "11:03")
       (subed-sort)
       (expect (buffer-string) :to-equal
               (concat
                "WEBVTT\n"
                "\n"
                "00:10:02.234 --> 00:02:10.345\n"
                "Bar.\n"
                "\n"
                "00:11:03.45 --> 00:03:15.5\n"
                "Baz.\n"
                "\n"
                "00:12:01.000 --> 00:01:05.123\n"
                "Foo.\n"))))
    (describe "point preservation"
      (it "works when subtitle text is non-empty."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (re-search-forward "01:01")
         (replace-match "12:01")
         (search-forward "\n")
         (expect (current-word) :to-equal "Foo")
         (subed-sort)
         (expect (current-word) :to-equal "Foo")))
      (it "works when subtitle text is empty."
        (with-temp-vtt-buffer
         (insert "WEBVTT\n\n00:12:01.000 --> 00:01:05.123\n")
         (let ((pos (point)))
           (subed-sort)
           (expect (buffer-string) :to-equal "WEBVTT\n\n00:12:01.000 --> 00:01:05.123\n\n")
           (expect (point) :to-equal pos))))
      (it "works in the header."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (goto-char (point-min))
         (subed-sort)
         (expect (point) :to-equal (point-min))))))
  (describe "Converting msecs to timestamp"
    (it "uses the right format"
      (with-temp-vtt-buffer
       (expect (subed-msecs-to-timestamp 1401) :to-equal "00:00:01.401"))))
  (describe "Getting the list of subtitles"
    (it "handles arrows and the lack of blank lines between cues."
      (with-temp-vtt-buffer
       ;; https://github.com/web-platform-tests/wpt/blob/master/webvtt/parsing/file-parsing/tests/support/arrows.vtt
       (insert "WEBVTT

-->
00:00:00.000 --> 00:00:01.000
text0
foo-->
00:00:00.000 --> 00:00:01.000
text1
-->foo
00:00:00.000 --> 00:00:01.000
text2
--->
00:00:00.000 --> 00:00:01.000
text3
-->-->
00:00:00.000 --> 00:00:01.000
text4
00:00:00.000 --> 00:00:01.000
text5

00:00:00.000 -a -->

00:00:00.000 --a -->

00:00:00.000 - -->

00:00:00.000 -- -->")
       (let ((list (subed-subtitle-list)))
         (expect (length list) :to-equal 6)
         (seq-map-indexed
          (lambda (cue i)
            (expect (elt cue 0) :to-equal "00:00:00.000")
            (expect (elt cue 3) :to-equal (format "text%d" i)))
          list))))
    (it "ignores things that look like comments in cue text."
      (with-temp-vtt-buffer
       (insert "WEBVTT

NOTE this is real comment that should be ignored

00:00:00.000 --> 00:00:01.000
NOTE text

NOTE
this is also a real comment that should be ignored
this is also a real comment that should be ignored

00:00:01.000 --> 00:00:02.000
NOTE text
NOTE text2")
       (let ((list (subed-subtitle-list)))
         (expect (elt (elt list 0) 3) :to-equal "NOTE text")
         (expect (elt (elt list 0) 4) :to-equal "this is real comment that should be ignored")
         (expect (elt (elt list 1) 3) :to-equal "NOTE text\nNOTE text2")
         (expect (elt (elt list 1) 4) :to-equal "this is also a real comment that should be ignored\nthis is also a real comment that should be ignored")))
      )
    )
  (describe "Working with comments"
    (before-each
      (setq mock-vtt-comments-data
            "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a test.

NOTE A comment can go here
and have more text as needed.

00:01:00.000 --> 00:00:02.000
This is another test here.
"))
    (it "ignores the comment when jumping to the end of the subtitle"
      (with-temp-vtt-buffer
       (insert mock-vtt-comments-data)
       (goto-char (point-min))
       (subed-forward-subtitle-end)
       (expect (current-word) :to-equal "test")
       (subed-forward-subtitle-end)
       (expect (current-word) :to-equal "here")))
    (describe "jumping to the comment"
      (it "returns nil when there is no comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (re-search-backward "This is a test")
         (expect (subed-jump-to-subtitle-comment) :to-be nil)))
      (it "jumps to the comment for the current subtitle."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (goto-char (point-max))
         (subed-jump-to-subtitle-comment)
         (expect (looking-at "NOTE A comment") :to-be t))))
    (describe "getting the comment"
      (it "returns nil when there is no comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (re-search-backward "This is a test")
         (expect (subed-subtitle-comment) :to-be nil)))
      (it "returns the comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (goto-char (point-max))
         (expect (subed-subtitle-comment) :to-equal "A comment can go here\nand have more text as needed."))))
    (describe "setting the comment"
      (it "sets the comment when there isn't one yet."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (re-search-backward "This is a test")
         (subed-set-subtitle-comment "Skip")
         (expect (buffer-string) :to-equal
                 "WEBVTT

NOTE Skip

00:00:00.000 --> 00:00:01.000
This is a test.

NOTE A comment can go here
and have more text as needed.

00:01:00.000 --> 00:00:02.000
This is another test here.
")))
      (it "replaces the comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (goto-char (point-max))
         (subed-set-subtitle-comment "Replaced.")
         (expect (buffer-string) :to-equal
                 "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a test.

NOTE Replaced.

00:01:00.000 --> 00:00:02.000
This is another test here.
")))
      (it "clears the comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (goto-char (point-max))
         (subed-set-subtitle-comment nil)
         (expect (buffer-string) :to-equal
                 "WEBVTT

00:00:00.000 --> 00:00:01.000
This is a test.

00:01:00.000 --> 00:00:02.000
This is another test here.
"))))
    (describe "going to the next subtitle's comment"
      (it "returns nil in an empty buffer."
        (with-temp-vtt-buffer
         (expect (subed-forward-subtitle-comment) :to-be nil)))
      (it "returns nil at the end of the file."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (expect (subed-forward-subtitle-comment) :to-be nil)))
      (it "returns nil if the next subtitle does not have a comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (save-excursion (subed-append-subtitle))
         (expect (subed-forward-subtitle-comment) :to-be nil)))
      (it "jumps to the next subtitle's comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (re-search-backward "This is a test")
         (expect (subed-forward-subtitle-comment) :not :to-be nil)
         (expect (looking-at "NOTE ") :to-be t))))
    (describe "going to the previous comment"
      (it "returns nil in an empty buffer."
        (with-temp-vtt-buffer
         (expect (subed-backward-subtitle-comment) :to-be nil)))
      (it "returns nil at the start of the file."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (goto-char (point-min))
         (expect (subed-backward-subtitle-comment) :to-be nil)))
      (it "returns nil if the previous subtitle does not have a comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (re-search-backward "This is another test here")
         (expect (subed-backward-subtitle-comment) :to-be nil)))
      (it "jumps to the previous subtitle's comment."
        (with-temp-vtt-buffer
         (insert mock-vtt-comments-data)
         (subed-append-subtitle)
         (expect (subed-backward-subtitle-comment) :not :to-be nil)
         (expect (looking-at "NOTE ") :to-be t))))
    (describe "when the cue text starts with Note"
      (it "is not confused."
        (with-temp-vtt-buffer
         (insert "WEBVTT

00:00:00.000 --> 00:00:00.999
Note this is a test

00:00:01.000 --> 00:00:01.000
another test
")
         (let ((case-fold-search t))
           (expect (elt (car (subed-subtitle-list)) 3) :to-equal "Note this is a test"))))))
  (describe "Merging with next subtitle"
    (it "throws an error in an empty buffer."
      (with-temp-vtt-buffer
       (expect (subed-merge-with-next) :to-throw 'error)))
    (it "throws an error with the last subtitle."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text 3)
       (expect (subed-merge-with-next) :to-throw 'error)))
    (it "combines the text and the time."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:02:02.234")
       (subed-merge-with-next)
       (expect (subed-subtitle-text) :to-equal "Bar. Baz.")
       (expect (subed-subtitle-msecs-start) :to-equal 122234)
       (expect (subed-subtitle-msecs-stop) :to-equal 195500)))
		(it "updates looping."
			(with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-jump-to-subtitle-text "00:02:02.234")
			 (let ((subed-loop-seconds-before 1)
						 (subed-loop-seconds-after 1))
				 (subed--set-subtitle-loop)
				 (expect subed--subtitle-loop-start :to-equal (subed-timestamp-to-msecs "00:02:01.234"))
				 (expect subed--subtitle-loop-stop :to-equal (subed-timestamp-to-msecs "00:02:11.345"))
				 (subed-merge-with-next)
				 (expect subed--subtitle-loop-start :to-equal (subed-timestamp-to-msecs "00:02:01.234"))
				 (expect subed--subtitle-loop-stop :to-equal (subed-timestamp-to-msecs "00:03:16.500"))))))
  (describe "Font-locking"
    (it "recognizes VTT syntax."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (font-lock-fontify-buffer)
       (goto-char (point-min))
       (re-search-forward "00:01:01")
       (expect (face-at-point) :to-equal 'subed-time-face)
       (re-search-forward "-->")
       (backward-char 1)
       (expect (face-at-point) :to-equal 'subed-time-separator-face))))
  (describe "with cues"
    (it "parses properly."
      (with-temp-vtt-buffer
       (insert "WEBVTT - This file has cues.

14
00:01:14.815 --> 00:01:18.114
- What?
- Where are we now?

15
00:01:18.171 --> 00:01:20.991
- This is big bat country.

16
00:01:21.058 --> 00:01:23.868
- [ Bats Screeching ]
- They won't get in your hair. They're after the bugs.")
       (expect (elt (car (subed-subtitle-list)) 3)
               :to-equal "- What?\n- Where are we now?"))))
  (describe "conversion"
    (it "creates TXT."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (with-current-buffer (subed-convert "TXT")
         (expect (buffer-string) :to-equal "Foo.\nBar.\nBaz.\n"))))
    (it "includes comments in TXT if requested."
      (with-temp-vtt-buffer
       (insert "WEBVTT

00:01:14.815 --> 00:01:18.114
Hello

NOTE Comment

00:01:18.171 --> 00:01:20.991
World

00:01:21.058 --> 00:01:23.868
Again")
       (with-current-buffer (subed-convert "TXT" t)
         (expect (buffer-string) :to-equal "Hello\n\nComment\n\nWorld\nAgain\n")))))
  (describe "iterating over subtitles"
    (describe "forwards"
      (it "handles headers."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (let (result)
           (subed-for-each-subtitle (point-min) (point-max) nil
             (add-to-list 'result (point)))
           (expect (length result) :to-equal 3))))
      (it "handles blank lines at the start of a caption."
        (with-temp-vtt-buffer
         (insert "WEBVTT
Kind: captions
Language: en

00:00:02.459 --> 00:00:05.610 align:start position:0%

hi<00:00:03.459><c> welcome</c><00:00:03.850><c> to</c><00:00:03.999><c> another</c><00:00:04.149><c> episode</c><00:00:04.509><c> of</c><00:00:05.020><c> Emacs</c>
")
         (let (result)
           (subed-for-each-subtitle (point-min) (point-max) nil
             (push (point) result))
           (expect (length result) :to-equal 1)))))
    (describe "backwards"
      (it "handles headers."
        (with-temp-vtt-buffer
         (insert mock-vtt-data)
         (let (result)
           (subed-for-each-subtitle (point-min) (point-max) t
             (add-to-list 'result (point)))
           (expect (length result) :to-equal 3))))
      (it "handles empty lines."
        (with-temp-vtt-buffer
         (insert mock-vtt-data "\n\n")
         (let (result)
           (subed-for-each-subtitle (point-min) (point-max) t
             (add-to-list 'result (point)))
           (expect (length result) :to-equal 3))))))
  (describe "timestamp at point"
    (it "returns nil if not at a timestamp."
      (with-temp-vtt-buffer
       (insert mock-vtt-data "\n\n")
       (subed-jump-to-subtitle-text)
       (expect (subed-timestamp-at-point) :to-be nil)))
    (it "returns a timestamp when at the start of a timestamp."
      (with-temp-vtt-buffer
       (insert mock-vtt-data "\n\n")
       (subed-jump-to-subtitle-time-start)
       (expect (subed-timestamp-at-point) :to-equal "00:03:03.45")))
    (it "returns a timestamp when in the middle of a timestamp."
      (with-temp-vtt-buffer
       (insert mock-vtt-data "\n\n")
       (subed-jump-to-subtitle-time-start)
       (forward-char 2)
       (expect (subed-timestamp-at-point) :to-equal "00:03:03.45")))
    (it "returns a timestamp when in a word timestamp."
      (with-temp-vtt-buffer
       (insert "WebVTT\n\n00:00:00.003 --> 00:00:05.123\nThis is <00:00:01.000>is a test\n\n")
       (re-search-backward "01\\.000")
       (forward-char 2)
       (expect (subed-timestamp-at-point) :to-equal "00:00:01.000"))))
  (describe "combining subtitles from multiple speakers"
    (it "works for subtitle lists."
      (expect
       (subed-vtt-combine-separate-speaker-files
        nil
        '(("Host"
           (nil 1000 2000 "Sentence A")
           (nil 4000 5000 "Sentence B"))
          ("Guest"
           (nil 500 2000 "Sentence 1")
           (nil 2500 2700 "Sentence 2")
           (nil 2701 2800 "Sentence 3"))))
       :to-equal
       '((nil 500 2000 "<v Guest>Sentence 1</v>")
         (nil 1000 2000 "<v Host>Sentence A</v>")
         (nil 2500 2700 "<v Guest>Sentence 2</v>")
         (nil 2701 2800 "<v Guest>Sentence 3</v>")
         (nil 4000 5000 "<v Host>Sentence B</v>"))))
    (it "works for subtitle files."
      (spy-on 'subed-parse-file :and-call-fake
              (lambda (filename &optional mode-func)
                      (if (string-match "host" filename)
                          '((nil 1000 2000 "Sentence A")
                            (nil 4000 5000 "Sentence B"))
                        '((nil 500 2000 "Sentence 1")
                          (nil 2500 2700 "Sentence 2")
                          (nil 2701 2800 "Sentence 3")))))
      (expect
       (subed-vtt-combine-separate-speaker-files
        nil
        '(("Host" . "/tmp/host.vtt")
          ("Guest" . "/tmp/guest.vtt")))
       :to-equal
       '((nil 500 2000 "<v Guest>Sentence 1</v>")
         (nil 1000 2000 "<v Host>Sentence A</v>")
         (nil 2500 2700 "<v Guest>Sentence 2</v>")
         (nil 2701 2800 "<v Guest>Sentence 3</v>")
         (nil 4000 5000 "<v Host>Sentence B</v>")))))
  (describe "inserting chapter comments"
    (it "adds a note to the first subtitle if specified."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-vtt-insert-chapter-comments "0:00 Introduction")
       (expect
        (buffer-string)
        :to-equal
        "WEBVTT

NOTE Introduction

00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.

00:03:03.45 --> 00:03:15.5
Baz.
")))
    (it "does not duplicate NOTEs."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (subed-vtt-insert-chapter-comments "0:00 Introduction")
       (subed-vtt-insert-chapter-comments "0:00 Introduction")
       (expect
        (buffer-string)
        :to-equal
        "WEBVTT

NOTE Introduction

00:01:01.000 --> 00:01:05.123
Foo.

00:02:02.234 --> 00:02:10.345
Bar.

00:03:03.45 --> 00:03:15.5
Baz.
")))
    (it "prepends the NOTE if there is an existing one."
      (with-temp-vtt-buffer
       (insert mock-vtt-data)
       (re-search-backward "Bar")
       (subed-set-subtitle-comment "#+COMMENT: Hello")
       (subed-vtt-insert-chapter-comments "2:00 Another test")
       (expect
        (buffer-string)
        :to-equal
        "WEBVTT

00:01:01.000 --> 00:01:05.123
Foo.

NOTE
Another test
#+COMMENT: Hello

00:02:02.234 --> 00:02:10.345
Bar.

00:03:03.45 --> 00:03:15.5
Baz.
")))
    (describe "when using clock time"
      (it "inserts the notes."
        (with-temp-vtt-buffer
         (subed-append-subtitle-list
          '((nil 0 300000 "Subtitle A.")
            (nil 600000 900000 "Subtitle B.")
            (nil 1200000 1500000 "Subtitle C.")
            (nil 1800000 2100000 "Subtitle D.")))
         (subed-set-file-clock-start "10:00:00")
         (subed-vtt-insert-chapter-comments-based-on-clock
          "10:12 Part 1
10:32 Part 2")
         (expect (buffer-string) :to-equal "00:00:00.000 --> 00:05:00.000
Subtitle A.

NOTE Part 1

00:10:00.000 --> 00:15:00.000
Subtitle B.

00:20:00.000 --> 00:25:00.000
Subtitle C.

NOTE Part 2

00:30:00.000 --> 00:35:00.000
Subtitle D.
")))))
  (describe "grouping subtitles by chapters"
    :var (chapters)
    (before-all
      (setq chapters
            (with-temp-vtt-buffer
             (subed-append-subtitle-list
              '((nil 1000 2000 "Cue 1.")
                (nil 3000 4000 "Cue 2.")
                (nil 4000 5000 "Cue 3." "Chapter Title A")
                (nil 6000 7000 "Cue 4.")
                (nil 8000 9000 "Cue 5." "Chapter Title B\n#+SCREENSHOT: test.jpg")
                (nil 4000 5000 "Cue 6." "Chapter Title A")))
             (subed-vtt-group-subtitles-by-chapter))))
    (it "includes subtitles before the first chapter."
      (expect (car (car chapters)) :to-be nil)
      (expect (elt (elt (cdr (car chapters)) 0) 3) :to-equal "Cue 1.")
      (expect (elt (elt (cdr (car chapters)) 1) 3) :to-equal "Cue 2."))
    (it "does not reuse chapters."
      (expect (length chapters) :to-equal 4)
      (expect (car (car (last chapters))) :to-equal "Chapter Title A"))
    (it "preserves other parts of the comment."
      (expect (elt (cadr (elt chapters 3))) :to-equal "Chapter Title B\n#+SCREENSHOT: test.jpg")
      ))
  (it "can replace [speaker-name]: with <v speaker-name>..</v>."
    (with-temp-vtt-buffer
     (subed-append-subtitle-list
      '((nil 1000 2000 "[Alice]: This is a test.")
        (nil 3000 4000 "[Bob]: This is a another test.")))
     (subed-vtt-replace-speaker-diarization-with-tags)
     (expect
      (mapcar (lambda (o) (elt o 3)) (subed-subtitle-list))
      :to-equal
      '("<v Alice>This is a test.</v>"
        "<v Bob>This is a another test.</v>")))))
