2019-10-25 22:00:47 +02:00
|
|
|
// Copyright 2019 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.
|
|
|
|
|
2019-10-30 01:22:06 +01:00
|
|
|
import 'package:path/path.dart' as p;
|
2019-10-25 22:00:47 +02:00
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
|
|
|
import 'package:sass_embedded/src/embedded_sass.pb.dart';
|
2021-01-07 00:56:18 +01:00
|
|
|
import 'package:sass_embedded/src/utils.dart';
|
2019-10-25 22:00:47 +02:00
|
|
|
|
|
|
|
import 'embedded_process.dart';
|
|
|
|
|
|
|
|
/// Returns a [InboundMessage] that compiles the given plain CSS
|
|
|
|
/// string.
|
2019-11-05 04:50:50 +01:00
|
|
|
InboundMessage compileString(String css,
|
2021-05-18 05:57:37 +02:00
|
|
|
{int? id,
|
|
|
|
bool? alertColor,
|
|
|
|
bool? alertAscii,
|
|
|
|
InboundMessage_Syntax? syntax,
|
|
|
|
InboundMessage_CompileRequest_OutputStyle? style,
|
|
|
|
String? url,
|
|
|
|
bool? sourceMap,
|
|
|
|
Iterable<InboundMessage_CompileRequest_Importer>? importers,
|
|
|
|
InboundMessage_CompileRequest_Importer? importer,
|
|
|
|
Iterable<String>? functions}) {
|
2019-11-05 04:50:50 +01:00
|
|
|
var input = InboundMessage_CompileRequest_StringInput()..source = css;
|
|
|
|
if (syntax != null) input.syntax = syntax;
|
|
|
|
if (url != null) input.url = url;
|
2020-12-23 23:49:33 +01:00
|
|
|
if (importer != null) input.importer = importer;
|
2019-11-05 04:50:50 +01:00
|
|
|
|
|
|
|
var request = InboundMessage_CompileRequest()..string = input;
|
2019-11-05 22:51:08 +01:00
|
|
|
if (id != null) request.id = id;
|
2019-11-08 01:03:47 +01:00
|
|
|
if (importers != null) request.importers.addAll(importers);
|
2019-11-05 04:50:50 +01:00
|
|
|
if (style != null) request.style = style;
|
|
|
|
if (sourceMap != null) request.sourceMap = sourceMap;
|
2019-11-09 00:25:42 +01:00
|
|
|
if (functions != null) request.globalFunctions.addAll(functions);
|
2021-02-04 03:31:27 +01:00
|
|
|
if (alertColor != null) request.alertColor = alertColor;
|
|
|
|
if (alertAscii != null) request.alertAscii = alertAscii;
|
2019-11-05 04:50:50 +01:00
|
|
|
|
|
|
|
return InboundMessage()..compileRequest = request;
|
|
|
|
}
|
2019-10-25 22:00:47 +02:00
|
|
|
|
|
|
|
/// Asserts that [process] emits a [ProtocolError] parse error with the given
|
|
|
|
/// [message] on its protobuf stream and prints a notice on stderr.
|
|
|
|
Future<void> expectParseError(EmbeddedProcess process, message) async {
|
|
|
|
await expectLater(process.outbound,
|
2021-01-07 00:56:18 +01:00
|
|
|
emits(isProtocolError(errorId, ProtocolError_ErrorType.PARSE, message)));
|
2019-11-09 00:25:42 +01:00
|
|
|
|
|
|
|
var stderrPrefix = "Host caused parse error: ";
|
|
|
|
await expectLater(
|
|
|
|
process.stderr,
|
|
|
|
message is String
|
|
|
|
? emitsInOrder("$stderrPrefix$message".split("\n"))
|
|
|
|
: emits(startsWith(stderrPrefix)));
|
2019-10-25 22:00:47 +02:00
|
|
|
}
|
|
|
|
|
2019-11-08 01:03:47 +01:00
|
|
|
/// Asserts that [process] emits a [ProtocolError] params error with the given
|
|
|
|
/// [message] on its protobuf stream and prints a notice on stderr.
|
|
|
|
Future<void> expectParamsError(EmbeddedProcess process, int id, message) async {
|
|
|
|
await expectLater(process.outbound,
|
|
|
|
emits(isProtocolError(id, ProtocolError_ErrorType.PARAMS, message)));
|
2019-11-09 00:25:42 +01:00
|
|
|
|
|
|
|
var stderrPrefix = "Host caused params error"
|
2021-01-07 00:56:18 +01:00
|
|
|
"${id == errorId ? '' : " with request $id"}: ";
|
2019-11-08 01:03:47 +01:00
|
|
|
await expectLater(
|
|
|
|
process.stderr,
|
2019-11-09 00:25:42 +01:00
|
|
|
message is String
|
|
|
|
? emitsInOrder("$stderrPrefix$message".split("\n"))
|
|
|
|
: emits(startsWith(stderrPrefix)));
|
2019-11-08 01:03:47 +01:00
|
|
|
}
|
|
|
|
|
2019-10-25 22:00:47 +02:00
|
|
|
/// Asserts that an [OutboundMessage] is a [ProtocolError] with the given [id],
|
|
|
|
/// [type], and optionally [message].
|
|
|
|
Matcher isProtocolError(int id, ProtocolError_ErrorType type, [message]) =>
|
|
|
|
predicate((value) {
|
|
|
|
expect(value, isA<OutboundMessage>());
|
|
|
|
var outboundMessage = value as OutboundMessage;
|
|
|
|
expect(outboundMessage.hasError(), isTrue,
|
2019-11-08 01:03:47 +01:00
|
|
|
reason: "Expected $outboundMessage to be a ProtocolError");
|
2019-10-25 22:00:47 +02:00
|
|
|
expect(outboundMessage.error.id, equals(id));
|
|
|
|
expect(outboundMessage.error.type, equals(type));
|
|
|
|
if (message != null) expect(outboundMessage.error.message, message);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2019-11-08 01:03:47 +01:00
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a
|
|
|
|
/// `CanonicalizeRequest` and returns it.
|
|
|
|
OutboundMessage_CanonicalizeRequest getCanonicalizeRequest(value) {
|
|
|
|
expect(value, isA<OutboundMessage>());
|
|
|
|
var message = value as OutboundMessage;
|
|
|
|
expect(message.hasCanonicalizeRequest(), isTrue,
|
|
|
|
reason: "Expected $message to have a CanonicalizeRequest");
|
|
|
|
return message.canonicalizeRequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a `ImportRequest` and
|
|
|
|
/// returns it.
|
|
|
|
OutboundMessage_ImportRequest getImportRequest(value) {
|
|
|
|
expect(value, isA<OutboundMessage>());
|
|
|
|
var message = value as OutboundMessage;
|
|
|
|
expect(message.hasImportRequest(), isTrue,
|
|
|
|
reason: "Expected $message to have a ImportRequest");
|
|
|
|
return message.importRequest;
|
|
|
|
}
|
|
|
|
|
2019-11-09 00:25:42 +01:00
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a
|
|
|
|
/// `FunctionCallRequest` and returns it.
|
|
|
|
OutboundMessage_FunctionCallRequest getFunctionCallRequest(value) {
|
|
|
|
expect(value, isA<OutboundMessage>());
|
|
|
|
var message = value as OutboundMessage;
|
|
|
|
expect(message.hasFunctionCallRequest(), isTrue,
|
|
|
|
reason: "Expected $message to have a FunctionCallRequest");
|
|
|
|
return message.functionCallRequest;
|
|
|
|
}
|
|
|
|
|
2019-11-05 04:50:50 +01:00
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a
|
|
|
|
/// `CompileResponse.Failure` and returns it.
|
|
|
|
OutboundMessage_CompileResponse_CompileFailure getCompileFailure(value) {
|
|
|
|
var response = getCompileResponse(value);
|
|
|
|
expect(response.hasFailure(), isTrue,
|
|
|
|
reason: "Expected $response to be a failure");
|
|
|
|
return response.failure;
|
|
|
|
}
|
|
|
|
|
2019-11-09 00:25:42 +01:00
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a
|
|
|
|
/// `CompileResponse.Success` and returns it.
|
|
|
|
OutboundMessage_CompileResponse_CompileSuccess getCompileSuccess(value) {
|
|
|
|
var response = getCompileResponse(value);
|
|
|
|
expect(response.hasSuccess(), isTrue,
|
|
|
|
reason: "Expected $response to be a success");
|
|
|
|
return response.success;
|
|
|
|
}
|
|
|
|
|
2019-11-05 04:50:50 +01:00
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a `CompileResponse` and
|
2019-10-25 22:00:47 +02:00
|
|
|
/// returns it.
|
|
|
|
OutboundMessage_CompileResponse getCompileResponse(value) {
|
|
|
|
expect(value, isA<OutboundMessage>());
|
|
|
|
var message = value as OutboundMessage;
|
|
|
|
expect(message.hasCompileResponse(), isTrue,
|
|
|
|
reason: "Expected $message to have a CompileResponse");
|
|
|
|
return message.compileResponse;
|
|
|
|
}
|
|
|
|
|
2019-11-05 22:51:08 +01:00
|
|
|
/// Asserts that [message] is an [OutboundMessage] with a `LogEvent` and
|
|
|
|
/// returns it.
|
|
|
|
OutboundMessage_LogEvent getLogEvent(value) {
|
|
|
|
expect(value, isA<OutboundMessage>());
|
|
|
|
var message = value as OutboundMessage;
|
|
|
|
expect(message.hasLogEvent(), isTrue,
|
|
|
|
reason: "Expected $message to have a LogEvent");
|
|
|
|
return message.logEvent;
|
|
|
|
}
|
|
|
|
|
2019-11-05 04:50:50 +01:00
|
|
|
/// Asserts that an [OutboundMessage] is a `CompileResponse` with CSS that
|
|
|
|
/// matches [css], with a source map that matches [sourceMap] (if passed).
|
2019-10-25 22:00:47 +02:00
|
|
|
///
|
|
|
|
/// If [css] is a [String], this automatically wraps it in
|
|
|
|
/// [equalsIgnoringWhitespace].
|
2019-11-08 01:03:47 +01:00
|
|
|
///
|
|
|
|
/// If [sourceMap] is a function, `response.success.sourceMap` is passed to it.
|
|
|
|
/// Otherwise, it's treated as a matcher for `response.success.sourceMap`.
|
2019-11-05 04:50:50 +01:00
|
|
|
Matcher isSuccess(css, {sourceMap}) => predicate((value) {
|
2019-11-09 00:25:42 +01:00
|
|
|
var success = getCompileSuccess(value);
|
|
|
|
expect(success.css, css is String ? equalsIgnoringWhitespace(css) : css);
|
2019-11-08 01:03:47 +01:00
|
|
|
if (sourceMap is void Function(String)) {
|
2019-11-09 00:25:42 +01:00
|
|
|
sourceMap(success.sourceMap);
|
2019-11-08 01:03:47 +01:00
|
|
|
} else if (sourceMap != null) {
|
2019-11-09 00:25:42 +01:00
|
|
|
expect(success.sourceMap, sourceMap);
|
2019-11-08 01:03:47 +01:00
|
|
|
}
|
2019-10-25 22:00:47 +02:00
|
|
|
return true;
|
|
|
|
});
|
2019-11-05 04:50:50 +01:00
|
|
|
|
|
|
|
/// Returns a [SourceSpan_SourceLocation] with the given [offset], [line], and
|
|
|
|
/// [column].
|
|
|
|
SourceSpan_SourceLocation location(int offset, int line, int column) =>
|
|
|
|
SourceSpan_SourceLocation()
|
|
|
|
..offset = offset
|
|
|
|
..line = line
|
|
|
|
..column = column;
|
|
|
|
|
|
|
|
/// Returns a matcher that verifies whether the given value refers to the same
|
|
|
|
/// path as [expected].
|
|
|
|
Matcher equalsPath(String expected) => predicate<String>(
|
|
|
|
(actual) => p.equals(actual, expected), "equals $expected");
|