hunk.code_window:         self.assertEqual(mail.reply_to, self.company_admin.partner_id.email_formatted)
           self.assertEqual(mail.state, 'outgoing')
           # test body content: KPIs
           body_html = html.fromstring(mail.body_html)
           kpi_values = body_html.xpath('//div[@data-field="sms"]//*[hasclass("kpi_value")]/text()')
           self.assertEqual(
               [t.strip().strip('%') for t in kpi_values],
               ['100', str(mailing.opened_ratio), str(mailing.replied_ratio)]
  
hunk.labels: keep keep keep keep replace keep keep keep
hunk.after_edit:         kpi_values = body_html.xpath('//table[@data-field="sms"]//*[hasclass("kpi_value")]/text()')
  
hunk.file_path: addons/test_mass_mailing/tests/test_mailing_statistics_sms.py
hunk.type: replace
file: /** @odoo-module */
  
  import { unaccent } from "./strings";
  
  /**
   * This private function computes a score that represent the fact that the
   * string contains the pattern, or not
   *
   * - If the score is 0, the string does not contain the letters of the pattern in
   *   the correct order.
   * - if the score is > 0, it actually contains the letters.
   *
   * Better matches will get a higher score: consecutive letters are better,
   * and a match closer to the beginning of the string is also scored higher.
   *
   * @param {string} pattern
   * @param {string} str
   * @returns {number}
   */
  function match(pattern, str) {
      let totalScore = 0;
      let currentScore = 0;
      const len = str.length;
      let patternIndex = 0;
  
      pattern = unaccent(pattern, false);
      str = unaccent(str, false);
  
      for (let i = 0; i < len; i++) {
          if (str[i] === pattern[patternIndex]) {
              patternIndex++;
              currentScore += 100 + currentScore - i / 200;
          } else {
              currentScore = 0;
          }
          totalScore = totalScore + currentScore;
      }
  
      return patternIndex === pattern.length ? totalScore : 0;
  }
  
  /**
   * Return a list of things that matches a pattern, ordered by their 'score' (
   * higher score first). An higher score means that the match is better. For
   * example, consecutive letters are considered a better match.
   *
   * @template T
   * @param {string} pattern
   * @param {T[]} list
   * @param {(element: T) => string} fn
   * @returns {T[]}
   */
  export function fuzzyLookup(pattern, list, fn) {
      const results = [];
      list.forEach((data) => {
          const score = match(pattern, fn(data));
          if (score > 0) {
              results.push({ score, elem: data });
          }
      });
  
      // we want better matches first
      results.sort((a, b) => b.score - a.score);
  
      return results.map((r) => r.elem);
  }
  
  // Does `pattern` fuzzy match `string`?
  /**
   * @param {string} pattern
   * @param {string} string
   * @returns {boolean}
   */
  export function fuzzyTest(pattern, string) {
      return match(pattern, string) !== 0;
  }
  