blob: b70fee467d32b1b665fc366d6b7d378400abdfff [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fuchsia_logger/logger.dart';
import 'package:mockito/mockito.dart';
// ignore_for_file: implementation_imports
import 'package:simple_browser/src/blocs/tabs_bloc.dart';
import 'package:simple_browser/src/blocs/webpage_bloc.dart';
import 'package:simple_browser/src/models/tabs_action.dart';
void main() {
setupLogger(name: 'tabs_bloc_test');
group('newTab', () {
test('Add 1 tab.', () async {
// Creates one tab.
final tb = await _creatNTabs(1);
// Sees if there is only one tab.
int actual = tb.tabs.length;
int expected = 1;
expect(
actual,
expected,
reason: _expectNTabsReason(
actual.toString(),
expected.toString(),
),
);
});
test('Add 2 tabs.', () async {
// Creates two tabs.
final tb = await _creatNTabs(2);
// Sees if there are two tabs.
int actual = tb.tabs.length;
int expected = 2;
expect(
actual,
expected,
reason: _expectNTabsReason(
actual.toString(),
expected.toString(),
),
);
});
});
group('focusTab', () {
test('Add 3 tabs, focus on the first tab and then the second tab.',
() async {
// Test Environment Set Up:
// Creates three tabs.
final tb = await _creatNTabs(3);
// Makes sure that the last tab is currently focused.
int actual = tb.currentTabIdx;
int expected = 2;
expect(
actual,
expected,
reason: _expectFocusedTabReason(
actual.toString(),
expected.toString(),
),
);
// Changes the focus to the first tab.
await _focusTab(tb, tb.tabs[0]);
// Sees if the index of the newly focused tab is 0.
actual = tb.currentTabIdx;
expected = 0;
expect(
actual,
expected,
reason: _expectFocusedTabReason(
actual.toString(),
expected.toString(),
),
);
// Changes the focus to the second tab.
await _focusTab(tb, tb.tabs[1]);
// Sees if the index of the newly focused tab is 1.
actual = tb.currentTabIdx;
expected = 1;
expect(
actual,
expected,
reason: _expectFocusedTabReason(
actual.toString(),
expected.toString(),
),
);
});
});
group('removeTab', () {
test('Add 1 tab and remove it.', () async {
// Test Environment Set Up:
// Creates one tab.
final tb = await _creatNTabs(1);
// Makes sure that there is only one tab.
int actual = tb.tabs.length;
int expected = 1;
expect(
actual,
expected,
reason: _expectNTabsReason(
actual.toString(),
expected.toString(),
),
);
// Closes the tab.
await _closeTab(tb, tb.currentTab);
// Sees if there is no tabs anyumore.
actual = tb.tabs.length;
expected = 0;
expect(
actual,
expected,
reason: _expectNTabsReason(
actual.toString(),
expected.toString(),
),
);
// Sees if the currentTab indicates null.
expect(
tb.currentTab,
null,
reason: _expectFocusedTabReason(
(tb.currentTabIdx).toString(),
'null',
),
);
});
test(
'Add 3 tabs, and remove the currently focused tab, which is the last tab.',
() async {
// Test Environment Set Up:
// Creates three tabs and focus on the first tab.
final tb = await _creatNTabs(3);
// Makes sure that the last tab is currently focused.
int actualFocusedTabIdx = tb.currentTabIdx;
int expectedFocusedTabIdx = 2;
expect(
actualFocusedTabIdx,
expectedFocusedTabIdx,
reason: _expectFocusedTabReason(
actualFocusedTabIdx.toString(),
expectedFocusedTabIdx.toString(),
),
);
// Saves the tab previous to the currently focused tab. (the second tab)
final expectedTab = tb.tabs[1];
// Closes the currently focused tab. (the last tab)
await _closeTab(tb, tb.currentTab);
// Sees if the number of the remaining tabs is 2.
int actualNumTabs = tb.tabs.length;
int expectedNumTabs = 2;
expect(
actualNumTabs,
expectedNumTabs,
reason: _expectNTabsReason(
actualNumTabs.toString(),
expectedNumTabs.toString(),
),
);
// Sees if the newly focused tab is the tab previous to the removed tab.
expect(tb.currentTab, expectedTab,
reason:
'''The currently focused tab is expected to be the previous one to the removed one,
but is actually not.''');
// Sees if the index of the newly focused tab is still 1.
actualFocusedTabIdx = tb.currentTabIdx;
expectedFocusedTabIdx = 1;
expect(
actualFocusedTabIdx,
expectedFocusedTabIdx,
reason: _expectFocusedTabReason(
actualFocusedTabIdx.toString(),
expectedFocusedTabIdx.toString(),
),
);
});
test('Add 3 tabs, focus on the first tab, and remove it.', () async {
// Test Environment Set Up:
// Creates three tabs and focus on the first tab.
final tb = await _creatNTabs(3);
await _focusTab(tb, tb.tabs[0]);
// Makes sure that the first tab is currently focused.
int actualFocusedTabIdx = tb.currentTabIdx;
int expectedFocusedTabIdx = 0;
expect(
actualFocusedTabIdx,
expectedFocusedTabIdx,
reason: _expectFocusedTabReason(
actualFocusedTabIdx.toString(),
expectedFocusedTabIdx.toString(),
),
);
// Saves the tab next to the currently focused tab. (the second tab)
final expectedTab = tb.tabs[1];
// Closes the currently focused tab. (the first tab)
await _closeTab(tb, tb.currentTab);
// Sees if the number of the remaining tabs is 2.
int actualNumTabs = tb.tabs.length;
int expectedNumTabs = 2;
expect(
actualNumTabs,
expectedNumTabs,
reason: _expectNTabsReason(
actualNumTabs.toString(),
expectedNumTabs.toString(),
),
);
// Sees if the newly focused tab is the tab next to the closed tab.
expect(tb.currentTab, expectedTab,
reason:
'''The currently focused tab is expected to be the next one to the removed one,
but is actually not.''');
// Sees if the index of the newly focused tab has been shifted to 0.
actualFocusedTabIdx = tb.currentTabIdx;
expectedFocusedTabIdx = 0;
expect(
actualFocusedTabIdx,
expectedFocusedTabIdx,
reason: _expectFocusedTabReason(
actualFocusedTabIdx.toString(),
expectedFocusedTabIdx.toString(),
),
);
});
test('Add 3 tabs and remove the first one.', () async {
// Test Environment Set Up:
// Creates three tabs.
final tb = await _creatNTabs(3);
// Makes sure that the last tab is currently focused.
int actualFocusedTabIdx = tb.currentTabIdx;
int expectedFocusedTabIdx = 2;
expect(
actualFocusedTabIdx,
expectedFocusedTabIdx,
reason: _expectFocusedTabReason(
actualFocusedTabIdx.toString(),
expectedFocusedTabIdx.toString(),
),
);
// Saves the currently focused tab. (the last tab)
final expectedTab = tb.currentTab;
// Closes the first tab.
await _closeTab(tb, tb.tabs[0]);
// Sees if the number of the remaining tabs is 2.
int actualNumTabs = tb.tabs.length;
int expectedNumTabs = 2;
expect(
actualNumTabs,
expectedNumTabs,
reason: _expectNTabsReason(
actualNumTabs.toString(),
expectedNumTabs.toString(),
),
);
// Sees if the currently focused tab is still the same one.
expect(tb.currentTab, expectedTab,
reason:
'''The focused tab is expected to be the same before and after closing the second tab,
but is actually different.''');
// Sees if the index of the currently focused tab has been shifted to 1.
actualFocusedTabIdx = tb.currentTabIdx;
expectedFocusedTabIdx = 1;
expect(
actualFocusedTabIdx,
expectedFocusedTabIdx,
reason: _expectFocusedTabReason(
actualFocusedTabIdx.toString(),
expectedFocusedTabIdx.toString(),
),
);
});
});
group('isOnlyTab, previousTab, and nextTab getters', () {
test(
'''isOnlyTab getter should return true when there is only one tab in the TabsBloc,
and return false when another tab is added.''', () async {
// Creates one tab.
final tb = await _creatNTabs(1);
// Sees if isOnlyTab getter returns true.
expect(
tb.isOnlyTab,
true,
reason:
'isOnlyTab is expected to be true, but is actually ${tb.isOnlyTab.toString()}.',
);
// Creates one more tab.
await _newTab(tb);
// Sees if isOnlyTab getter returns false.
expect(
tb.isOnlyTab,
false,
reason:
'isOnlyTab is expected to be false, but is actually ${tb.isOnlyTab.toString()}.',
);
});
test(
'''previousTab getter should return the previous tab to the currently focused tab,
and the nextTab getter should return the next tab to the currently focused tab.''',
() async {
// Creates two tabs.
final tb = await _creatNTabs(2);
// Sees if the previousTab getter returns the first tab.
expect(
tb.previousTab,
tb.tabs.first,
reason: '''previousTab is expected to be the first tab,
but is actually ${tb.tabs.indexOf(tb.previousTab)}th tab.''',
);
// Changes the focus to the first tab.
await _focusTab(tb, tb.tabs[0]);
// Sees if the nextTab getter returns the second(last) tab.
expect(
tb.nextTab,
tb.tabs.last,
reason: '''nextTab is expected to be the last tab,
but is actually ${tb.tabs.indexOf(tb.nextTab)}th tab.''',
);
});
});
}
Future<TabsBloc> _creatNTabs(int n) async {
TabsBloc tb = TabsBloc(
tabFactory: () => MockWebPageBloc(),
disposeTab: (tab) => tab.dispose(),
);
for (int i = 0; i < n; i++) {
await _newTab(tb);
}
return tb;
}
String _expectNTabsReason(String actual, String expected) =>
'TabsBloc is expected to have $expected tabs, but actually has $actual tabs.';
String _expectFocusedTabReason(String actual, String expected) =>
'The index of the currently focused tab is expected to be $expected, but is actually $actual.';
/// awaits for a single callback from a [Listenable]
Future _awaitListenable(Listenable listenable) {
final c = Completer();
Function l;
l = () {
c.complete();
listenable.removeListener(l);
};
listenable.addListener(l);
return c.future;
}
/// sends an [TabsAction] to [TabsBloc] and awaits for a callback in a [Listenable]
Future _addActionAndAwait(
TabsBloc tb,
Listenable listenable,
TabsAction action,
) async {
tb.request.add(action);
await _awaitListenable(listenable);
}
/// adds a new tab to a [TabsBloc] and awaits completion with [TabsBloc.tabs]
Future _newTab(TabsBloc tb) => _addActionAndAwait(
tb,
tb.tabsNotifier,
NewTabAction(),
);
/// sets focus to a tab in [TabsBloc] and awaits completion with [TabsBloc.currentTab]
Future _focusTab(TabsBloc tb, WebPageBloc tab) => _addActionAndAwait(
tb,
tb.currentTabNotifier,
FocusTabAction(tab: tab),
);
Future _closeTab(TabsBloc tb, WebPageBloc tab) => _addActionAndAwait(
tb,
tb.tabsNotifier,
RemoveTabAction(tab: tab),
);
class MockWebPageBloc extends Mock implements WebPageBloc {}