Skip to content

Commit 7f55807

Browse files
authored
Merge pull request flutter#2 from ditman/add-mock-adsense-tests
Some more tweaks!
2 parents 308a325 + a36f9f8 commit 7f55807

12 files changed

+400
-226
lines changed

packages/google_adsense/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ To start displaying ads, initialize the AdSense with your [client/publisher ID](
2424
import 'package:google_adsense/google_adsense.dart';
2525
2626
void main() {
27-
adSense.initialize('your_ad_client_id');
27+
adSense.initialize('0556581589806023'); // TODO: Replace with your own AdClient ID
2828
runApp(const MyApp());
2929
}
3030
@@ -33,10 +33,11 @@ You are all set to start displaying [Auto ads](https://support.google.com/adsens
3333
#### Display ad unit Widget
3434
<?code-excerpt "example/lib/main.dart (adUnit)"?>
3535
```dart
36-
adSense.adUnit(AdUnitConfiguration.displayAdUnit(
37-
adSlot: 'your_ad_slot_id',
38-
adFormat: AdFormat.AUTO,
39-
isFullWidthResponsive: false))
36+
adSense.adUnit(AdUnitConfiguration.displayAdUnit(
37+
adSlot: '4773943862', // TODO: Replace with your own AdSlot ID
38+
adFormat: AdFormat
39+
.AUTO, // Remove AdFormat to make ads limited by height
40+
))
4041
```
4142

4243
#### Customize ad unit Widget

packages/google_adsense/example/integration_test/ad_widget_test.dart

Lines changed: 121 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,161 @@
66
// 1. Run chrome driver with --port=4444
77
// 2. Run the test from example folder with: flutter drive -d web-server --web-port 7357 --browser-name chrome --driver test_driver/integration_test.dart --target integration_test/ad_widget_test.dart
88

9-
import 'package:flutter/widgets.dart';
9+
import 'package:flutter/material.dart';
1010
import 'package:flutter_test/flutter_test.dart';
11-
import 'package:google_adsense/google_adsense.dart';
11+
// Ensure we don't use the singleton `adSense`, but the local copies to this plugin.
12+
import 'package:google_adsense/google_adsense.dart' hide adSense;
1213
import 'package:integration_test/integration_test.dart';
1314
import 'package:web/web.dart' as web;
1415

16+
import 'test_js_interop.dart';
17+
1518
const String testClient = 'test_client';
1619
const String testSlot = 'test_slot';
17-
late AdSense adsense;
20+
const String testScriptUrl =
21+
'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-$testClient';
1822

1923
void main() async {
2024
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
2125

26+
late AdSense adsense;
27+
2228
setUp(() async {
2329
adsense = AdSense();
2430
});
2531

32+
tearDown(() {
33+
clearAdsByGoogleMock();
34+
});
35+
2636
group('initialization', () {
27-
testWidgets('Initialization adds AdSense snippet to index.html',
28-
(WidgetTester _) async {
37+
testWidgets('Initialization adds AdSense snippet.', (WidgetTester _) async {
38+
final web.HTMLElement target = web.HTMLDivElement();
2939
// Given
30-
const String expectedScriptUrl =
31-
'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-$testClient';
3240

33-
// When
34-
adsense.initialize(testClient);
41+
adsense.initialize(testClient, jsLoaderTarget: target);
3542

36-
// Then
37-
final web.HTMLScriptElement injected =
38-
web.document.head!.lastChild! as web.HTMLScriptElement;
39-
expect(injected.src, expectedScriptUrl);
43+
final web.HTMLScriptElement? injected =
44+
target.lastElementChild as web.HTMLScriptElement?;
45+
46+
expect(injected, isNotNull);
47+
expect(injected!.src, testScriptUrl);
4048
expect(injected.crossOrigin, 'anonymous');
4149
expect(injected.async, true);
4250
});
51+
52+
testWidgets('Skips initialization if script already present.',
53+
(WidgetTester _) async {
54+
final web.HTMLScriptElement script = web.HTMLScriptElement()
55+
..id = 'previously-injected'
56+
..src = testScriptUrl;
57+
final web.HTMLElement target = web.HTMLDivElement()..appendChild(script);
58+
59+
adsense.initialize(testClient, jsLoaderTarget: target);
60+
61+
expect(target.childElementCount, 1);
62+
expect(target.firstElementChild?.id, 'previously-injected');
63+
});
64+
65+
testWidgets('Skips initialization if adsense object already present.',
66+
(WidgetTester _) async {
67+
final web.HTMLElement target = web.HTMLDivElement();
68+
69+
// Write an empty noop object
70+
mockAdsByGoogle(() {});
71+
72+
adsense.initialize(testClient, jsLoaderTarget: target);
73+
74+
expect(target.firstElementChild, isNull);
75+
});
4376
});
4477

4578
group('adWidget', () {
46-
testWidgets('AdUnitWidget is created and rendered',
79+
testWidgets('Filled ad units resize widget height',
4780
(WidgetTester tester) async {
4881
// When
49-
// TODO(sokoloff06): Mock server response
82+
mockAdsByGoogle(() {
83+
// Locate the target element, and push a red div to it...
84+
final web.Element? adTarget =
85+
web.document.querySelector('div[id^=adUnit] ins');
86+
87+
final web.HTMLElement fakeAd = web.HTMLDivElement()
88+
..style.width = '320px'
89+
..style.height = '137px'
90+
..style.background = 'red';
91+
92+
adTarget!
93+
..appendChild(fakeAd)
94+
..setAttribute('data-ad-status', AdStatus.FILLED);
95+
});
5096

5197
adsense.initialize(testClient);
98+
5299
final Widget adUnitWidget =
53-
adSense.adUnit(AdUnitConfiguration.displayAdUnit(adSlot: testSlot));
54-
await tester.pumpWidget(adUnitWidget);
55-
await tester.pumpWidget(
56-
adUnitWidget); // TODO(sokoloff06): Why only works when pumping twice?
100+
adsense.adUnit(AdUnitConfiguration.displayAdUnit(adSlot: testSlot));
101+
102+
await pumpAdWidget(adUnitWidget, tester);
103+
104+
// Then
105+
// Widget level
106+
expect(find.byWidget(adUnitWidget), findsOneWidget);
107+
108+
final Size size = tester.getSize(find.byWidget(adUnitWidget));
109+
110+
expect(size.height, 137);
111+
});
112+
113+
testWidgets('Unfilled ad units collapse widget height',
114+
(WidgetTester tester) async {
115+
// When
116+
mockAdsByGoogle(() {
117+
// Locate the target element, and push a red div to it...
118+
final web.Element? adTarget =
119+
web.document.querySelector('div[id^=adUnit] ins');
120+
121+
// The internal styling of the Ad doesn't matter, if AdSense tells us it is UNFILLED.
122+
final web.HTMLElement fakeAd = web.HTMLDivElement()
123+
..style.width = '320px'
124+
..style.height = '137px'
125+
..style.background = 'red';
126+
127+
adTarget!
128+
..appendChild(fakeAd)
129+
..setAttribute('data-ad-status', AdStatus.UNFILLED);
130+
});
131+
132+
adsense.initialize(testClient);
133+
final Widget adUnitWidget =
134+
adsense.adUnit(AdUnitConfiguration.displayAdUnit(adSlot: testSlot));
135+
136+
await pumpAdWidget(adUnitWidget, tester);
137+
57138
// Then
58139
// Widget level
59140
expect(find.byWidget(adUnitWidget), findsOneWidget);
60-
expect(adUnitWidget, isA<Widget>());
61141

62-
// DOM level
63-
final web.HTMLElement? platformView =
64-
web.document.querySelector('flt-platform-view') as web.HTMLElement?;
65-
expect(platformView, isNotNull);
66-
final web.HTMLElement ins =
67-
platformView!.querySelector('ins')! as web.HTMLElement;
68-
expect(ins.style.display, 'block');
142+
final Size size = tester.getSize(find.byWidget(adUnitWidget));
69143

70-
// TODO(sokoloff06): Validate response is rendered
144+
expect(size.height, 0);
71145
});
72146
});
73147
}
148+
149+
// Pumps an AdUnit Widget into a given tester, with some parameters
150+
Future<void> pumpAdWidget(Widget adUnit, WidgetTester tester) async {
151+
await tester.pumpWidget(
152+
MaterialApp(
153+
home: Scaffold(
154+
body: Center(
155+
child: adUnit,
156+
),
157+
),
158+
),
159+
);
160+
161+
// This extra pump is needed for the platform view to actually render in the DOM.
162+
await tester.pump();
163+
164+
// This extra pump is needed to simulate the async behavior of the adsense JS mock.
165+
await tester.pumpAndSettle();
166+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:google_adsense/google_adsense.dart';
7+
import 'package:integration_test/integration_test.dart';
8+
import 'package:web/web.dart' as web;
9+
10+
const String testClient = 'test_client';
11+
12+
void main() async {
13+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
14+
15+
// We test this separately so we don't have to worry about removing the script
16+
// from the page. Tests in `ad_widget_test.dart` use overrides in `initialize`
17+
// to keep them self-contained.
18+
group('JS initialization', () {
19+
testWidgets('Initialization adds AdSense snippet.', (WidgetTester _) async {
20+
// Given
21+
const String expectedScriptUrl =
22+
'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-$testClient';
23+
24+
// When (using the singleton adSense from the plugin)
25+
adSense.initialize(testClient);
26+
27+
// Then
28+
final web.HTMLScriptElement? injected =
29+
web.document.head?.lastElementChild as web.HTMLScriptElement?;
30+
31+
expect(injected, isNotNull);
32+
expect(injected!.src, expectedScriptUrl);
33+
expect(injected.crossOrigin, 'anonymous');
34+
expect(injected.async, true);
35+
});
36+
});
37+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@JS()
6+
library;
7+
8+
import 'dart:async';
9+
import 'dart:js_interop';
10+
11+
// window.adsbygoogle uses "duck typing", so let us set anything to it.
12+
@JS('adsbygoogle')
13+
external set _adsbygoogle(JSAny? value);
14+
15+
/// Mocks `adsbygoogle` [push] function.
16+
///
17+
/// `push` will run in the next tick (`Timer.run`) to ensure async behavior.
18+
void mockAdsByGoogle(void Function() push) {
19+
_adsbygoogle = <String, Object>{
20+
'push': () {
21+
Timer.run(push);
22+
}.toJS,
23+
}.jsify();
24+
}
25+
26+
/// Sets `adsbygoogle` to null.
27+
void clearAdsByGoogleMock() {
28+
_adsbygoogle = null;
29+
}

packages/google_adsense/example/lib/main.dart

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'package:flutter/material.dart';
88
import 'package:google_adsense/google_adsense.dart';
99

1010
void main() {
11-
adSense.initialize('your_ad_client_id');
11+
adSense.initialize('0556581589806023'); // TODO: Replace with your own AdClient ID
1212
runApp(const MyApp());
1313
}
1414

@@ -56,40 +56,54 @@ class _MyHomePageState extends State<MyHomePage> {
5656
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
5757
title: const Text('AdSense for Flutter demo app'),
5858
),
59-
body: Center(
60-
child: Column(
61-
mainAxisAlignment: MainAxisAlignment.center,
62-
children: <Widget>[
63-
const Text(
64-
'You have pushed the button this many times:',
65-
),
66-
Row(
67-
children: <Widget>[
68-
const Text('Some text'),
69-
Expanded(
70-
child:
71-
// #docregion adUnit
72-
adSense.adUnit(AdUnitConfiguration.displayAdUnit(
73-
adSlot: 'your_ad_slot_id',
74-
adFormat: AdFormat.AUTO,
75-
isFullWidthResponsive: false))
76-
// #enddocregion adUnit
77-
,
78-
)
79-
],
80-
),
81-
Text(
82-
'$_counter',
83-
style: Theme.of(context).textTheme.headlineMedium,
84-
),
85-
],
59+
body: SingleChildScrollView(
60+
child: Center(
61+
child: Column(
62+
mainAxisAlignment: MainAxisAlignment.center,
63+
children: <Widget>[
64+
const Text(
65+
'Responsive Ad Constrained by width of 150px:',
66+
),
67+
Container(
68+
constraints: const BoxConstraints(maxWidth: 150),
69+
padding: const EdgeInsets.only(bottom: 10),
70+
child:
71+
// #docregion adUnit
72+
adSense.adUnit(AdUnitConfiguration.displayAdUnit(
73+
adSlot: '4773943862', // TODO: Replace with your own AdSlot ID
74+
adFormat: AdFormat
75+
.AUTO, // Remove AdFormat to make ads limited by height
76+
))
77+
// #enddocregion adUnit
78+
,
79+
),
80+
const Text(
81+
'Responsive Ad Constrained by height of 100px:',
82+
),
83+
Container(
84+
constraints: const BoxConstraints(maxHeight: 100),
85+
padding: const EdgeInsets.only(bottom: 10),
86+
child: adSense.adUnit(AdUnitConfiguration.displayAdUnit(
87+
adSlot: '4773943862', // TODO: Replace with your own AdSlot ID
88+
// adFormat: AdFormat.AUTO, // Not using AdFormat to make ad unit respect height constraint
89+
)),
90+
),
91+
const Text(
92+
'Fixed 125x125 size Ad:',
93+
),
94+
Container(
95+
height: 125,
96+
width: 125,
97+
padding: const EdgeInsets.only(bottom: 10),
98+
child: adSense.adUnit(AdUnitConfiguration.displayAdUnit(
99+
adSlot: '8937810400',
100+
// adFormat: AdFormat.AUTO, // Not using AdFormat to make ad unit respect height constraint
101+
isFullWidthResponsive: false)),
102+
),
103+
],
104+
),
86105
),
87106
),
88-
floatingActionButton: FloatingActionButton(
89-
onPressed: _incrementCounter,
90-
tooltip: 'Increment',
91-
child: const Icon(Icons.add),
92-
), // This trailing comma makes auto-formatting nicer for build methods.
93107
);
94108
}
95109
}

packages/google_adsense/lib/google_adsense.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
// found in the LICENSE file.
44
export 'src/ad_unit_configuration.dart';
55
export 'src/ad_unit_params.dart';
6-
export 'src/adsense_stub.dart' if (dart.library.html) 'src/adsense_web.dart';
6+
export 'src/adsense_stub.dart'
7+
if (dart.library.js_interop) 'src/adsense_web.dart';

0 commit comments

Comments
 (0)