• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

yext / answers-search-ui / 27959158937

18 Jun 2026 05:51PM UTC coverage: 61.924% (+0.009%) from 61.915%
27959158937

Pull #1996

github

mkouzel-yext
feat: add AI signpost for GDA

Adds an signpost to the built in GenerativeDirectAnswer card to inform users that the direct answer content is AI-generated. The content of the signpost is customizable, but defaults are provided. The signposting is require to comply with upcoming EU regulations, and is therefore on by default.

J=[WAT-5646](https://yext.atlassian.net/browse/WAT-5646)
TEST=manual,auto

Tests added for GDA signpost.
Pull Request #1996: feat: add AI signpost for GDA

2051 of 3457 branches covered (59.33%)

Branch coverage included in aggregate %.

16 of 16 new or added lines in 1 file covered. (100.0%)

336 existing lines in 12 files now uncovered.

3511 of 5525 relevant lines covered (63.55%)

27.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

39.34
/src/ui/speechrecognition/speechrecognizer.js
1
/* eslint-disable new-cap */
2
import { isMicrosoftEdge, isSafari } from '../../core/utils/useragent';
3
import { transformSpeechRecognitionLocaleForEdge } from '../../core/speechrecognition/locales';
4
import TranslationFlagger from '../i18n/translationflagger';
5
import alert from '../alert';
6

7
/**
8
 * Responsible for recognizing speech
9
 */
10
export default class SpeechRecognizer {
11
  constructor (locale) {
12
    this._speechRecognition = new window.webkitSpeechRecognition();
2✔
13
    this._speechRecognition.interimResults = true;
2✔
14

15
    this._speechRecognition.lang = isMicrosoftEdge()
2!
16
      ? transformSpeechRecognitionLocaleForEdge(locale)
17
      : locale;
18

19
    /**
20
     * The latest result from speech recognition
21
     * @type {string}
22
     */
23
    this._latestResult = '';
2✔
24

25
    /**
26
     * The timeout ID of the silence threshold timeout
27
     * @type {number|null}
28
     */
29
    this._silenceThresholdTimeout = null;
2✔
30

31
    /**
32
     * The amount of silence after the last detected word before triggering a search.
33
     * This is primarily used for Safari since Edge and Chrome typically detect the end of
34
     * voice search phrase very quickly.
35
     */
36
    this._silenceThresholdToSearch = 1000;
2✔
37

38
    /**
39
     * Indicates that speech recognition is currently active.
40
     * This is used to prevent the speech recognizer from starting while it is already active.
41
     * @type {boolean}
42
     */
43
    this._recognitionActive = false;
2✔
44

45
    this._init();
2✔
46
  }
47

48
  _init () {
49
    this._speechRecognition.addEventListener('start', () => {
2✔
50
      this._recognitionActive = true;
1✔
51
    });
52
    this._speechRecognition.addEventListener('end', () => {
2✔
UNCOV
53
      this._recognitionActive = false;
×
54
      this._onCompleteHandler && this._onCompleteHandler(this._latestResult);
×
55
    });
56
    this._speechRecognition.addEventListener('result', event => {
2✔
UNCOV
57
      this._handleResultEvent(event);
×
58
    });
59
    this._speechRecognition.addEventListener('error', event => {
2✔
UNCOV
60
      this._handleErrorEvent(event);
×
61
    });
62
  }
63

64
  /**
65
   * Handles a search recognition result event
66
   * @param {SpeechRecognitionEvent} event
67
   */
68
  _handleResultEvent (event) {
69
    /**
70
     * Set a timeout to stop the speech recognition if no words are detected for the configured amount
71
     * of time. Every time a result comes in, reset that timeout.
72
     */
UNCOV
73
    clearTimeout(this._silenceThresholdTimeout);
×
74
    this._silenceThresholdTimeout = setTimeout(() => {
×
75
      this.stop();
×
76
    }, this._silenceThresholdToSearch);
77

UNCOV
78
    const result = event.results[0][0].transcript;
×
79
    const isFinalResult = event.results[0].isFinal;
×
80

UNCOV
81
    if (result !== this._latestResult) {
×
82
      this._onResultHandler && this._onResultHandler(result);
×
83
      this._latestResult = result;
×
84
    }
UNCOV
85
    if (isFinalResult) {
×
86
      this.stop();
×
87
    }
88
  }
89

90
  /**
91
   * Handles a speech recognition error
92
   * @param {SpeechRecognitionError} event
93
   */
94
  _handleErrorEvent (event) {
UNCOV
95
    if (event.error === 'service-not-allowed') {
×
96
      let errorMsg = TranslationFlagger.flag({ phrase: 'Speech Recognition is not available.' });
×
97
      if (isSafari()) {
×
98
        errorMsg += '\n' + TranslationFlagger.flag({ phrase: 'Note: For Safari users, Siri must be enabled' });
×
99
      }
UNCOV
100
      alert(errorMsg);
×
101
    } else {
UNCOV
102
      console.warn(event);
×
103
    }
UNCOV
104
    this._onError && this._onError();
×
105
  }
106

107
  /**
108
   * Starts the speech recognizer if it has not already started
109
   */
110
  start () {
111
    try {
1✔
112
      !this._recognitionActive && this._speechRecognition.start();
1✔
113
    } catch (err) {
UNCOV
114
      console.warn(err);
×
115
    }
116
  }
117

118
  /**
119
   * Stops the speech recognizer if it is active
120
   */
121
  stop () {
122
    this._recognitionActive && this._speechRecognition.stop();
1✔
123
  }
124

125
  /**
126
   * Sets the callback which is called when an error occurs
127
   * @param {Function} cb
128
   */
129
  onError (cb) {
130
    this._onError = cb;
2✔
131
  }
132

133
  /**
134
   * Sets the callback which is called when a result is detected
135
   * @param {Function} cb
136
   */
137
  onResult (cb) {
138
    this._onResultHandler = cb;
2✔
139
  }
140

141
  /**
142
   * Sets the callback which is called when speech recognition is complete
143
   * @param {Function} cb
144
   */
145
  onComplete (cb) {
146
    this._onCompleteHandler = cb;
2✔
147
  }
148
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc