blob: cba3e5d4fc96c559b73ba5f8b04a650f32890367 [file] [edit]
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:shadow_sources/src/directive_extractor.dart';
import 'package:test/test.dart';
void main() {
group('DirectiveOccurrence model', () {
test('reports kind and helper getters correctly', () {
final importOcc = DirectiveOccurrence(
kind: .import,
uri: 'package:foo/foo.dart',
offset: 10,
length: 22,
rawLexeme: "'package:foo/foo.dart'",
);
expect(importOcc.isImport, isTrue);
expect(importOcc.isExport, isFalse);
expect(importOcc.isPart, isFalse);
expect(importOcc.isPartOf, isFalse);
expect(
importOcc.toString(),
equals(
'DirectiveOccurrence(DirectiveKind.import: package:foo/foo.dart, offset: 10, len: 22)',
),
);
final exportOcc = DirectiveOccurrence(
kind: .export,
uri: 'package:foo/foo.dart',
offset: 0,
length: 22,
rawLexeme: "'package:foo/foo.dart'",
);
expect(exportOcc.isExport, isTrue);
expect(exportOcc.isImport, isFalse);
final partOcc = DirectiveOccurrence(
kind: .part,
uri: 'src/part.dart',
offset: 0,
length: 15,
rawLexeme: "'src/part.dart'",
);
expect(partOcc.isPart, isTrue);
final partOfOcc = DirectiveOccurrence(
kind: .partOf,
uri: 'parent.dart',
offset: 0,
length: 13,
rawLexeme: "'parent.dart'",
);
expect(partOfOcc.isPartOf, isTrue);
});
});
group('extractDirectives', () {
test(
'extracts imports, exports, parts, and part-ofs with token offsets',
() {
const code = '''
import 'package:collection/collection.dart';
export 'src/helper.dart' show foo;
part 'src/part_file.dart';
part of 'src/parent.dart';
void main() {}
''';
final directives = extractDirectives(code);
expect(directives.length, equals(4));
expect(directives[0].isImport, isTrue);
expect(directives[0].uri, equals('package:collection/collection.dart'));
expect(
directives[0].rawLexeme,
equals("'package:collection/collection.dart'"),
);
expect(
code.substring(
directives[0].offset,
directives[0].offset + directives[0].length,
),
equals(directives[0].rawLexeme),
);
expect(directives[1].isExport, isTrue);
expect(directives[1].uri, equals('src/helper.dart'));
expect(directives[1].rawLexeme, equals("'src/helper.dart'"));
expect(
code.substring(
directives[1].offset,
directives[1].offset + directives[1].length,
),
equals(directives[1].rawLexeme),
);
expect(directives[2].isPart, isTrue);
expect(directives[2].uri, equals('src/part_file.dart'));
expect(directives[2].rawLexeme, equals("'src/part_file.dart'"));
expect(directives[3].isPartOf, isTrue);
expect(directives[3].uri, equals('src/parent.dart'));
expect(directives[3].rawLexeme, equals("'src/parent.dart'"));
},
);
test(
'handles imports and exports with aliases, combinators, and deferred',
() {
const code = '''
import 'package:collection/collection.dart' as col show min, max;
export 'package:collection/collection.dart' hide binarySearch;
import 'package:async/async.dart' deferred as a_sync;
''';
final directives = extractDirectives(code);
expect(directives.length, equals(3));
expect(directives[0].isImport, isTrue);
expect(directives[0].uri, equals('package:collection/collection.dart'));
expect(directives[1].isExport, isTrue);
expect(directives[1].uri, equals('package:collection/collection.dart'));
expect(directives[2].isImport, isTrue);
expect(directives[2].uri, equals('package:async/async.dart'));
},
);
test('handles single, double, triple, and raw quotes correctly', () {
const code = """
import 'package:single/single.dart';
import "package:double/double.dart";
import '''package:triple_single/ts.dart''';
import \"\"\"package:triple_double/td.dart\"\"\";
import r'package:raw_single/rs.dart';
import r"package:raw_double/rd.dart";
import r'''package:raw_triple_single/rts.dart''';
import r\"\"\"package:raw_triple_double/rtd.dart\"\"\";
""";
final directives = extractDirectives(code);
expect(directives.length, equals(8));
expect(directives[0].uri, equals('package:single/single.dart'));
expect(directives[1].uri, equals('package:double/double.dart'));
expect(directives[2].uri, equals('package:triple_single/ts.dart'));
expect(directives[3].uri, equals('package:triple_double/td.dart'));
expect(directives[4].uri, equals('package:raw_single/rs.dart'));
expect(directives[5].uri, equals('package:raw_double/rd.dart'));
expect(directives[6].uri, equals('package:raw_triple_single/rts.dart'));
expect(directives[7].uri, equals('package:raw_triple_double/rtd.dart'));
});
test('handles conditional imports and exports', () {
const code = '''
import 'package:foo/foo.dart'
if (dart.library.io) 'package:foo/foo_io.dart'
if (dart.library.js_interop == 'true') 'package:foo/foo_web.dart';
export 'package:bar/bar.dart'
if (dart.library.html) 'package:bar/bar_html.dart';
''';
final directives = extractDirectives(code);
expect(directives.length, equals(5));
expect(directives[0].isImport, isTrue);
expect(directives[0].uri, equals('package:foo/foo.dart'));
expect(directives[1].isImport, isTrue);
expect(directives[1].uri, equals('package:foo/foo_io.dart'));
expect(directives[2].isImport, isTrue);
expect(directives[2].uri, equals('package:foo/foo_web.dart'));
expect(directives[3].isExport, isTrue);
expect(directives[3].uri, equals('package:bar/bar.dart'));
expect(directives[4].isExport, isTrue);
expect(directives[4].uri, equals('package:bar/bar_html.dart'));
});
test('does not extract string literals outside top-level directives', () {
const code = '''
import 'package:foo/foo.dart';
const str = 'package:not_a_directive/test.dart';
void main() {
final map = {'key': "value"};
print('Hello world');
}
''';
final directives = extractDirectives(code);
expect(directives.length, equals(1));
expect(directives[0].uri, equals('package:foo/foo.dart'));
});
test('ignores named part of directive without string URI', () {
const code = '''
part of my_library;
void foo() {}
''';
final directives = extractDirectives(code);
expect(directives, isEmpty);
});
});
group('rewriteLexeme', () {
test('preserves quotation formats', () {
expect(
rewriteLexeme("'package:old/old.dart'", 'package:new/new.dart'),
equals("'package:new/new.dart'"),
);
expect(
rewriteLexeme('"package:old/old.dart"', 'package:new/new.dart'),
equals('"package:new/new.dart"'),
);
expect(
rewriteLexeme("'''package:old/old.dart'''", 'package:new/new.dart'),
equals("'''package:new/new.dart'''"),
);
expect(
rewriteLexeme('"""package:old/old.dart"""', 'package:new/new.dart'),
equals('"""package:new/new.dart"""'),
);
expect(
rewriteLexeme("r'package:old/old.dart'", 'package:new/new.dart'),
equals("r'package:new/new.dart'"),
);
expect(
rewriteLexeme('r"package:old/old.dart"', 'package:new/new.dart'),
equals('r"package:new/new.dart"'),
);
expect(
rewriteLexeme("r'''package:old/old.dart'''", 'package:new/new.dart'),
equals("r'''package:new/new.dart'''"),
);
expect(
rewriteLexeme('r"""package:old/old.dart"""', 'package:new/new.dart'),
equals('r"""package:new/new.dart"""'),
);
});
});
group('rewriteDirective', () {
test(
'rewrites directive token within content without touching surroundings',
() {
const code = '''
// Header comment
import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
void main() {}
''';
final directives = extractDirectives(code);
final collectionDirective = directives[0];
final rewritten = rewriteDirective(
code,
collectionDirective,
Uri.parse('package:collection/shadowed_collection.dart'),
);
expect(
rewritten,
equals('''
// Header comment
import 'package:collection/shadowed_collection.dart';
import 'package:meta/meta.dart';
void main() {}
'''),
);
},
);
});
}