content: // Copyright 2024 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'package:test/test.dart';
  
  import 'package:sass/src/util/string.dart';
  import 'package:sass/src/util/map.dart';
  
  void main() {
    group("toCssIdentifier()", () {
      group("doesn't escape", () {
        test(
          'a double hyphen',
          () => expect('--'.toCssIdentifier(), equals('--')),
        );
  
        group("a starting character", () {
          const chars = {
            'lower-case alphabetic': 'q',
            'upper-case alphabetic': 'E',
            'an underscore': '_',
            'non-ASCII': 'ä',
            'double-width': '👭',
          };
  
          group("at the very beginning that's", () {
            for (var (name, char) in chars.pairs) {
              test(name, () => expect(char.toCssIdentifier(), equals(char)));
            }
          });
  
          group("after a single hyphen that's", () {
            for (var (name, char) in chars.pairs) {
              test(
                name,
                () => expect('-$char'.toCssIdentifier(), equals('-$char')),
              );
            }
          });
        });
  
        group("a middle character", () {
          const chars = {
            'lower-case alphabetic': 'q',
            'upper-case alphabetic': 'E',
            'numeric': '4',
            'an underscore': '_',
            'a hyphen': '-',
            'non-ASCII': 'ä',
            'double-width': '👭',
          };
  
          group("after a name start that's", () {
            for (var (name, char) in chars.pairs) {
              test(
                name,
                () => expect('a$char'.toCssIdentifier(), equals('a$char')),
              );
            }
          });
  
          group("after a double hyphen that's", () {
            for (var (name, char) in chars.pairs) {
              test(
                name,
                () => expect('--$char'.toCssIdentifier(), equals('--$char')),
              );
            }
          });
        });
      });
  
      group('escapes', () {
        test(
          'a single hyphen',
          () => expect('-'.toCssIdentifier(), equals('\\2d')),
        );
  
        group('a starting character', () {
          const chars = {
            'numeric': ('4', '\\34'),
            'non-alphanumeric ASCII': ('%', '\\25'),
            'a BMP private-use character': ('\ueabc', '\\eabc'),
            'a supplementary private-use character': ('\u{fabcd}', '\\fabcd'),
          };
  
          group("at the very beginning that's", () {
            for (var (name, (char, escape)) in chars.pairs) {
              test(name, () => expect(char.toCssIdentifier(), equals(escape)));
            }
          });
  
          group("after a single hyphen that's", () {
            for (var (name, (char, escape)) in chars.pairs) {
              test(
                name,
                () => expect('-$char'.toCssIdentifier(), equals('-$escape')),
              );
            }
          });
        });
  
        group('a middle character', () {
          const chars = {
            'non-alphanumeric ASCII': ('%', '\\25'),
            'a BMP private-use character': ('\ueabc', '\\eabc'),
            'a supplementary private-use character': ('\u{fabcd}', '\\fabcd'),
          };
  
          group("after a name start that's", () {
            for (var (name, (char, escape)) in chars.pairs) {
              test(
                name,
                () => expect('a$char'.toCssIdentifier(), equals('a$escape')),
              );
            }
          });
  
          group("after a double hyphen that's", () {
            for (var (name, (char, escape)) in chars.pairs) {
              test(
                name,
                () => expect('--$char'.toCssIdentifier(), equals('--$escape')),
              );
            }
          });
        });
      });
  
      group('throws an error for', () {
        test(
          'the empty string',
          () => expect(''.toCssIdentifier, throwsFormatException),
        );
  
        const chars = {
          'zero': '\u0000',
          'single high surrogate': '\udabc',
          'single low surrogate': '\udcde',
        };
  
        group("a starting character that's", () {
          for (var (name, char) in chars.pairs) {
            test(name, () => expect(char.toCssIdentifier, throwsFormatException));
          }
        });
  
        group("after a hyphen that's", () {
          for (var (name, char) in chars.pairs) {
            test(
              name,
              () => expect('-$char'.toCssIdentifier, throwsFormatException),
            );
          }
        });
  
        group("after a name start that's", () {
          for (var (name, char) in chars.pairs) {
            test(
              name,
              () => expect('a$char'.toCssIdentifier, throwsFormatException),
            );
          }
        });
  
        group("after a double hyphen that's", () {
          for (var (name, char) in chars.pairs) {
            test(
              name,
              () => expect('--$char'.toCssIdentifier, throwsFormatException),
            );
          }
        });
  
        group("before a body char that's", () {
          for (var (name, char) in chars.pairs) {
            test(
              name,
              () => expect('a${char}b'.toCssIdentifier, throwsFormatException),
            );
          }
        });
      });
  
      group('adds a space between an escape and', () {
        test('a digit', () => expect(' 1'.toCssIdentifier(), '\\20 1'));
  
        test(
          'a lowercase hex letter',
          () => expect(' b'.toCssIdentifier(), '\\20 b'),
        );
  
        test(
          'an uppercase hex letter',
          () => expect(' B'.toCssIdentifier(), '\\20 B'),
        );
      });
  
      group('doesn\'t add a space between an escape and', () {
        test(
          'the end of the string',
          () => expect(' '.toCssIdentifier(), '\\20'),
        );
  
        test(
          'a lowercase non-hex letter',
          () => expect(' g'.toCssIdentifier(), '\\20g'),
        );
  
        test(
          'an uppercase non-hex letter',
          () => expect(' G'.toCssIdentifier(), '\\20G'),
        );
  
        test('a hyphen', () => expect(' -'.toCssIdentifier(), '\\20-'));
  
        test(
          'a non-ascii character',
          () => expect(' ä'.toCssIdentifier(), '\\20ä'),
        );
  
        test('another escape', () => expect('  '.toCssIdentifier(), '\\20\\20'));
      });
    });
  }
  