mirror of
https://github.com/hmalik144/Weather-apps.git
synced 2025-12-10 02:05:20 +00:00
- Testsuite expansion
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -65,6 +65,7 @@ gen-external-apklibs
|
|||||||
/out/
|
/out/
|
||||||
|
|
||||||
# User-specific configurations
|
# User-specific configurations
|
||||||
|
.idea/androidTestResultsUserPreferences.xml
|
||||||
.idea/caches/
|
.idea/caches/
|
||||||
.idea/libraries/
|
.idea/libraries/
|
||||||
.idea/shelf/
|
.idea/shelf/
|
||||||
@@ -94,5 +95,3 @@ gen-external-apklibs
|
|||||||
/fastlane/report.xml
|
/fastlane/report.xml
|
||||||
# Google play files
|
# Google play files
|
||||||
/google-play-key.json
|
/google-play-key.json
|
||||||
|
|
||||||
/.idea/androidTestResultsUserPreferences.xml
|
|
||||||
|
|||||||
237
.idea/androidTestResultsUserPreferences.xml
generated
237
.idea/androidTestResultsUserPreferences.xml
generated
@@ -1,237 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AndroidTestResultsUserPreferences">
|
|
||||||
<option name="androidTestResultsTableState">
|
|
||||||
<map>
|
|
||||||
<entry key="-2146704034">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-2113309033">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="2C121FDH300122" value="120" />
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Google Android SDK built for x86" value="120" />
|
|
||||||
<entry key="Google Pixel 7 Pro" value="120" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
<entry key="emulator-5554" value="120" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-2008434490">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-1906103057">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-1721686438">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-1578868619">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-860247611">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="2C121FDH300122" value="120" />
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Google Pixel 7 Pro" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="-409920851">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="108569748">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="2C121FDH300122" value="120" />
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Google Pixel 7 Pro" value="120" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="110413981">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="170536241">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="287238248">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="408375334">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="721647317">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="1127175145">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="1256180664">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="1440597283">
|
|
||||||
<value>
|
|
||||||
<AndroidTestResultsTableState>
|
|
||||||
<option name="preferredColumnWidths">
|
|
||||||
<map>
|
|
||||||
<entry key="Duration" value="90" />
|
|
||||||
<entry key="Pixel_2_API_27" value="120" />
|
|
||||||
<entry key="Tests" value="360" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</AndroidTestResultsTableState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
218
Gemfile.lock
Normal file
218
Gemfile.lock
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
CFPropertyList (3.0.6)
|
||||||
|
rexml
|
||||||
|
addressable (2.8.5)
|
||||||
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
|
artifactory (3.0.15)
|
||||||
|
atomos (0.1.3)
|
||||||
|
aws-eventstream (1.2.0)
|
||||||
|
aws-partitions (1.798.0)
|
||||||
|
aws-sdk-core (3.180.1)
|
||||||
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
aws-partitions (~> 1, >= 1.651.0)
|
||||||
|
aws-sigv4 (~> 1.5)
|
||||||
|
jmespath (~> 1, >= 1.6.1)
|
||||||
|
aws-sdk-kms (1.71.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.177.0)
|
||||||
|
aws-sigv4 (~> 1.1)
|
||||||
|
aws-sdk-s3 (1.132.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.179.0)
|
||||||
|
aws-sdk-kms (~> 1)
|
||||||
|
aws-sigv4 (~> 1.6)
|
||||||
|
aws-sigv4 (1.6.0)
|
||||||
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
babosa (1.0.4)
|
||||||
|
claide (1.1.0)
|
||||||
|
colored (1.2)
|
||||||
|
colored2 (3.1.2)
|
||||||
|
commander (4.6.0)
|
||||||
|
highline (~> 2.0.0)
|
||||||
|
declarative (0.0.20)
|
||||||
|
digest-crc (0.6.5)
|
||||||
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
|
domain_name (0.5.20190701)
|
||||||
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
dotenv (2.8.1)
|
||||||
|
emoji_regex (3.2.3)
|
||||||
|
excon (0.100.0)
|
||||||
|
faraday (1.10.3)
|
||||||
|
faraday-em_http (~> 1.0)
|
||||||
|
faraday-em_synchrony (~> 1.0)
|
||||||
|
faraday-excon (~> 1.1)
|
||||||
|
faraday-httpclient (~> 1.0)
|
||||||
|
faraday-multipart (~> 1.0)
|
||||||
|
faraday-net_http (~> 1.0)
|
||||||
|
faraday-net_http_persistent (~> 1.0)
|
||||||
|
faraday-patron (~> 1.0)
|
||||||
|
faraday-rack (~> 1.0)
|
||||||
|
faraday-retry (~> 1.0)
|
||||||
|
ruby2_keywords (>= 0.0.4)
|
||||||
|
faraday-cookie_jar (0.0.7)
|
||||||
|
faraday (>= 0.8.0)
|
||||||
|
http-cookie (~> 1.0.0)
|
||||||
|
faraday-em_http (1.0.0)
|
||||||
|
faraday-em_synchrony (1.0.0)
|
||||||
|
faraday-excon (1.1.0)
|
||||||
|
faraday-httpclient (1.0.1)
|
||||||
|
faraday-multipart (1.0.4)
|
||||||
|
multipart-post (~> 2)
|
||||||
|
faraday-net_http (1.0.1)
|
||||||
|
faraday-net_http_persistent (1.2.0)
|
||||||
|
faraday-patron (1.0.0)
|
||||||
|
faraday-rack (1.0.0)
|
||||||
|
faraday-retry (1.0.3)
|
||||||
|
faraday_middleware (1.2.0)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
fastimage (2.2.7)
|
||||||
|
fastlane (2.214.0)
|
||||||
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
|
addressable (>= 2.8, < 3.0.0)
|
||||||
|
artifactory (~> 3.0)
|
||||||
|
aws-sdk-s3 (~> 1.0)
|
||||||
|
babosa (>= 1.0.3, < 2.0.0)
|
||||||
|
bundler (>= 1.12.0, < 3.0.0)
|
||||||
|
colored
|
||||||
|
commander (~> 4.6)
|
||||||
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
|
emoji_regex (>= 0.1, < 4.0)
|
||||||
|
excon (>= 0.71.0, < 1.0.0)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
|
faraday_middleware (~> 1.0)
|
||||||
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
|
google-apis-androidpublisher_v3 (~> 0.3)
|
||||||
|
google-apis-playcustomapp_v1 (~> 0.1)
|
||||||
|
google-cloud-storage (~> 1.31)
|
||||||
|
highline (~> 2.0)
|
||||||
|
json (< 3.0.0)
|
||||||
|
jwt (>= 2.1.0, < 3)
|
||||||
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
|
multipart-post (>= 2.0.0, < 3.0.0)
|
||||||
|
naturally (~> 2.2)
|
||||||
|
optparse (~> 0.1.1)
|
||||||
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
|
rubyzip (>= 2.0.0, < 3.0.0)
|
||||||
|
security (= 0.1.3)
|
||||||
|
simctl (~> 1.6.3)
|
||||||
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
|
terminal-table (>= 1.4.5, < 2.0.0)
|
||||||
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
|
word_wrap (~> 1.0.0)
|
||||||
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
|
xcpretty (~> 0.3.0)
|
||||||
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
|
gh_inspector (1.1.3)
|
||||||
|
google-apis-androidpublisher_v3 (0.46.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-apis-core (0.11.1)
|
||||||
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
|
httpclient (>= 2.8.1, < 3.a)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
representable (~> 3.0)
|
||||||
|
retriable (>= 2.0, < 4.a)
|
||||||
|
rexml
|
||||||
|
webrick
|
||||||
|
google-apis-iamcredentials_v1 (0.17.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-apis-playcustomapp_v1 (0.13.0)
|
||||||
|
google-apis-core (>= 0.11.0, < 2.a)
|
||||||
|
google-apis-storage_v1 (0.19.0)
|
||||||
|
google-apis-core (>= 0.9.0, < 2.a)
|
||||||
|
google-cloud-core (1.6.0)
|
||||||
|
google-cloud-env (~> 1.0)
|
||||||
|
google-cloud-errors (~> 1.0)
|
||||||
|
google-cloud-env (1.6.0)
|
||||||
|
faraday (>= 0.17.3, < 3.0)
|
||||||
|
google-cloud-errors (1.3.1)
|
||||||
|
google-cloud-storage (1.44.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
digest-crc (~> 0.4)
|
||||||
|
google-apis-iamcredentials_v1 (~> 0.1)
|
||||||
|
google-apis-storage_v1 (~> 0.19.0)
|
||||||
|
google-cloud-core (~> 1.6)
|
||||||
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
googleauth (1.7.0)
|
||||||
|
faraday (>= 0.17.3, < 3.a)
|
||||||
|
jwt (>= 1.4, < 3.0)
|
||||||
|
memoist (~> 0.16)
|
||||||
|
multi_json (~> 1.11)
|
||||||
|
os (>= 0.9, < 2.0)
|
||||||
|
signet (>= 0.16, < 2.a)
|
||||||
|
highline (2.0.3)
|
||||||
|
http-cookie (1.0.5)
|
||||||
|
domain_name (~> 0.5)
|
||||||
|
httpclient (2.8.3)
|
||||||
|
jmespath (1.6.2)
|
||||||
|
json (2.6.3)
|
||||||
|
jwt (2.7.1)
|
||||||
|
memoist (0.16.2)
|
||||||
|
mini_magick (4.12.0)
|
||||||
|
mini_mime (1.1.2)
|
||||||
|
multi_json (1.15.0)
|
||||||
|
multipart-post (2.3.0)
|
||||||
|
nanaimo (0.3.0)
|
||||||
|
naturally (2.2.1)
|
||||||
|
optparse (0.1.1)
|
||||||
|
os (1.1.4)
|
||||||
|
plist (3.7.0)
|
||||||
|
public_suffix (5.0.3)
|
||||||
|
rake (13.0.6)
|
||||||
|
representable (3.2.0)
|
||||||
|
declarative (< 0.1.0)
|
||||||
|
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||||
|
uber (< 0.2.0)
|
||||||
|
retriable (3.1.2)
|
||||||
|
rexml (3.2.6)
|
||||||
|
rouge (2.0.7)
|
||||||
|
ruby2_keywords (0.0.5)
|
||||||
|
rubyzip (2.3.2)
|
||||||
|
security (0.1.3)
|
||||||
|
signet (0.17.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
faraday (>= 0.17.5, < 3.a)
|
||||||
|
jwt (>= 1.5, < 3.0)
|
||||||
|
multi_json (~> 1.10)
|
||||||
|
simctl (1.6.10)
|
||||||
|
CFPropertyList
|
||||||
|
naturally
|
||||||
|
terminal-notifier (2.0.0)
|
||||||
|
terminal-table (1.8.0)
|
||||||
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
|
trailblazer-option (0.1.2)
|
||||||
|
tty-cursor (0.7.1)
|
||||||
|
tty-screen (0.8.1)
|
||||||
|
tty-spinner (0.9.3)
|
||||||
|
tty-cursor (~> 0.7)
|
||||||
|
uber (0.1.0)
|
||||||
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.8.2)
|
||||||
|
unicode-display_width (1.8.0)
|
||||||
|
webrick (1.8.1)
|
||||||
|
word_wrap (1.0.0)
|
||||||
|
xcodeproj (1.22.0)
|
||||||
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
|
atomos (~> 0.1.3)
|
||||||
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
nanaimo (~> 0.3.0)
|
||||||
|
rexml (~> 3.2.4)
|
||||||
|
xcpretty (0.3.0)
|
||||||
|
rouge (~> 2.0.7)
|
||||||
|
xcpretty-travis-formatter (1.0.1)
|
||||||
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
x86_64-linux
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
fastlane
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.3.5
|
||||||
@@ -15,6 +15,7 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
|||||||
@Test
|
@Test
|
||||||
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
fun loadApp_invalidKeyWeatherResponse_returnsEmptyViewPage() {
|
||||||
homeScreen {
|
homeScreen {
|
||||||
|
waitFor(2000)
|
||||||
// verify empty
|
// verify empty
|
||||||
verifyUnableToRetrieve()
|
verifyUnableToRetrieve()
|
||||||
}
|
}
|
||||||
@@ -23,6 +24,7 @@ class HomePageNoDataUITest : BaseTest<MainActivity>(MainActivity::class.java) {
|
|||||||
@Test
|
@Test
|
||||||
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
fun invalidKeyWeatherResponse_swipeToRefresh_returnsValidPage() {
|
||||||
homeScreen {
|
homeScreen {
|
||||||
|
waitFor(2000)
|
||||||
// verify empty
|
// verify empty
|
||||||
verifyUnableToRetrieve()
|
verifyUnableToRetrieve()
|
||||||
|
|
||||||
|
|||||||
@@ -34,15 +34,6 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
|
||||||
navigateToFurtherDetails(it)
|
|
||||||
})
|
|
||||||
|
|
||||||
forecast_listview.apply {
|
|
||||||
layoutManager = LinearLayoutManager(context)
|
|
||||||
adapter = recyclerAdapter
|
|
||||||
}
|
|
||||||
|
|
||||||
swipe_refresh.apply {
|
swipe_refresh.apply {
|
||||||
setOnRefreshListener {
|
setOnRefreshListener {
|
||||||
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
|
getPermissionResult(ACCESS_COARSE_LOCATION, LOCATION_PERMISSION_REQUEST) {
|
||||||
@@ -51,16 +42,14 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(error: Any?) {
|
recyclerAdapter = WeatherRecyclerAdapter(itemClick = {
|
||||||
swipe_refresh.isRefreshing = false
|
navigateToFurtherDetails(it)
|
||||||
}
|
})
|
||||||
|
|
||||||
override fun onSuccess(data: Any?) {
|
forecast_listview.apply {
|
||||||
swipe_refresh.isRefreshing = false
|
layoutManager = LinearLayoutManager(context)
|
||||||
if (data is WeatherDisplay) {
|
adapter = recyclerAdapter
|
||||||
recyclerAdapter.addCurrent(data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +62,19 @@ class HomeFragment : BaseFragment<MainViewModel>(R.layout.fragment_home) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(data: Any?) {
|
||||||
|
super.onSuccess(data)
|
||||||
|
swipe_refresh.isRefreshing = false
|
||||||
|
if (data is WeatherDisplay) {
|
||||||
|
recyclerAdapter.addCurrent(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(error: Any?) {
|
||||||
|
super.onFailure(error)
|
||||||
|
swipe_refresh.isRefreshing = false
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
override fun permissionsGranted() {
|
override fun permissionsGranted() {
|
||||||
viewModel.fetchData()
|
viewModel.fetchData()
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.appttude.h_mal.atlas_weather.R
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||||
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
import com.appttude.h_mal.atlas_weather.model.forecast.WeatherDisplay
|
||||||
import com.appttude.h_mal.atlas_weather.utils.generateView
|
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||||
|
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast.ViewHolderForecast
|
||||||
|
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecastDaily.ViewHolderForecastDaily
|
||||||
|
|
||||||
class WeatherRecyclerAdapter(
|
class WeatherRecyclerAdapter(
|
||||||
val itemClick: (Forecast) -> Unit
|
private val itemClick: (Forecast) -> Unit
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
var weather: WeatherDisplay? = null
|
var weather: WeatherDisplay? = null
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
fun addCurrent(current: WeatherDisplay) {
|
fun addCurrent(current: WeatherDisplay) {
|
||||||
weather = current
|
weather = current
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
@@ -32,8 +31,8 @@ class WeatherRecyclerAdapter(
|
|||||||
ViewHolderCurrent(viewCurrent)
|
ViewHolderCurrent(viewCurrent)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Forecast -> {
|
is ViewType.ForecastHourly -> {
|
||||||
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
val viewForecast = parent.generateView(R.layout.hourly_item_forecast)
|
||||||
ViewHolderForecast(viewForecast)
|
ViewHolderForecast(viewForecast)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,13 +40,19 @@ class WeatherRecyclerAdapter(
|
|||||||
val viewFurther = parent.generateView(R.layout.list_item_further)
|
val viewFurther = parent.generateView(R.layout.list_item_further)
|
||||||
ViewHolderFurtherDetails(viewFurther)
|
ViewHolderFurtherDetails(viewFurther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ViewType.ForecastDaily -> {
|
||||||
|
val viewForecast = parent.generateView(R.layout.list_item_forecast)
|
||||||
|
ViewHolderForecastDaily(viewForecast)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ViewType {
|
sealed class ViewType {
|
||||||
object Empty : ViewType()
|
object Empty : ViewType()
|
||||||
object Current : ViewType()
|
object Current : ViewType()
|
||||||
object Forecast : ViewType()
|
object ForecastHourly : ViewType()
|
||||||
|
object ForecastDaily : ViewType()
|
||||||
object Further : ViewType()
|
object Further : ViewType()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,19 +60,20 @@ class WeatherRecyclerAdapter(
|
|||||||
return when (type) {
|
return when (type) {
|
||||||
0 -> ViewType.Empty
|
0 -> ViewType.Empty
|
||||||
1 -> ViewType.Current
|
1 -> ViewType.Current
|
||||||
2 -> ViewType.Forecast
|
2 -> ViewType.ForecastHourly
|
||||||
3 -> ViewType.Further
|
3 -> ViewType.Further
|
||||||
|
4 -> ViewType.ForecastDaily
|
||||||
else -> ViewType.Empty
|
else -> ViewType.Empty
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
if (weather == null) return 0
|
if (weather == null) return 0
|
||||||
|
|
||||||
return when (position) {
|
return when (position) {
|
||||||
0 -> 1
|
0 -> 1
|
||||||
in 1 until itemCount - 2 -> 2
|
1 -> 3
|
||||||
itemCount - 1 -> 3
|
2 -> 2
|
||||||
|
in 3 until (itemCount) -> 4
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,28 +90,31 @@ class WeatherRecyclerAdapter(
|
|||||||
viewHolderCurrent.bindData(weather)
|
viewHolderCurrent.bindData(weather)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewType.Forecast -> {
|
|
||||||
val viewHolderForecast = holder as ViewHolderForecast
|
|
||||||
|
|
||||||
weather?.forecast?.get(position - 1)?.let { i ->
|
|
||||||
viewHolderForecast.bindView(i)
|
|
||||||
viewHolderForecast.itemView.setOnClickListener {
|
|
||||||
itemClick(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is ViewType.Further -> {
|
is ViewType.Further -> {
|
||||||
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
val viewHolderCurrent = holder as ViewHolderFurtherDetails
|
||||||
viewHolderCurrent.bindData(weather)
|
viewHolderCurrent.bindData(weather)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ViewType.ForecastHourly -> {
|
||||||
|
val viewHolderForecast = holder as ViewHolderForecast
|
||||||
|
viewHolderForecast.bindView(weather?.hourly)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ViewType.ForecastDaily -> {
|
||||||
|
val viewHolderForecast = holder as ViewHolderForecastDaily
|
||||||
|
weather?.forecast?.getOrNull(position - 3)?.let { f ->
|
||||||
|
viewHolderForecast.bindView(f)
|
||||||
|
viewHolderForecast.itemView.setOnClickListener {
|
||||||
|
itemClick.invoke(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
if (weather == null) return 0
|
return if (weather == null) 1 else 3 + (weather?.forecast?.size ?: 0)
|
||||||
return 2 + (weather?.forecast?.size ?: 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.appttude.h_mal.monoWeather.ui.home.adapter.forecast
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||||
|
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||||
|
import com.appttude.h_mal.atlas_weather.utils.toTime
|
||||||
|
|
||||||
|
class GridCellHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
|
var dayTV: TextView = itemView.findViewById(R.id.widget_item_day)
|
||||||
|
var weatherIV: ImageView = itemView.findViewById(R.id.widget_item_image)
|
||||||
|
var mainTempTV: TextView = itemView.findViewById(R.id.widget_item_temp_high)
|
||||||
|
|
||||||
|
fun bindView(hour: Hour?) {
|
||||||
|
dayTV.text = hour?.dt?.toTime()
|
||||||
|
weatherIV.loadImage(hour?.icon)
|
||||||
|
mainTempTV.text = hour?.temp?.toInt()?.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||||
|
import com.appttude.h_mal.atlas_weather.utils.generateView
|
||||||
|
import com.appttude.h_mal.monoWeather.ui.home.adapter.forecast.GridCellHolder
|
||||||
|
|
||||||
|
class GridForecastAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
var weather: MutableList<Hour> = mutableListOf()
|
||||||
|
|
||||||
|
fun addCurrent(current: List<Hour>?) {
|
||||||
|
weather.clear()
|
||||||
|
current?.let { weather.addAll(it) }
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
val viewCurrent = parent.generateView(R.layout.hourly_forecast_grid_item)
|
||||||
|
return GridCellHolder(viewCurrent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
val view = holder as GridCellHolder
|
||||||
|
val forecast = weather[position]
|
||||||
|
view.bindView(forecast)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = weather.size
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.appttude.h_mal.atlas_weather.R
|
||||||
|
import com.appttude.h_mal.atlas_weather.model.weather.Hour
|
||||||
|
import com.appttude.h_mal.atlas_weather.ui.home.adapter.forecast.GridForecastAdapter
|
||||||
|
|
||||||
|
class ViewHolderForecast(
|
||||||
|
itemView: View
|
||||||
|
) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
|
var recyclerView: RecyclerView = itemView.findViewById(R.id.forecast_recyclerview)
|
||||||
|
|
||||||
|
fun bindView(forecasts: List<Hour>?) {
|
||||||
|
val adapter = GridForecastAdapter()
|
||||||
|
adapter.addCurrent(forecasts)
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.appttude.h_mal.atlas_weather.ui.home.adapter
|
package com.appttude.h_mal.atlas_weather.ui.home.adapter.forecastDaily
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
@@ -8,21 +8,21 @@ import com.appttude.h_mal.atlas_weather.R
|
|||||||
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
import com.appttude.h_mal.atlas_weather.model.forecast.Forecast
|
||||||
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
import com.appttude.h_mal.atlas_weather.utils.loadImage
|
||||||
|
|
||||||
class ViewHolderForecast(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
class ViewHolderForecastDaily(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
var dateTV: TextView = itemView.findViewById(R.id.list_date)
|
var dateTV: TextView = itemView.findViewById(R.id.list_date)
|
||||||
var dayTV: TextView = itemView.findViewById(R.id.list_day)
|
var dayTV: TextView = itemView.findViewById(R.id.list_day)
|
||||||
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
|
||||||
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
|
var weatherIV: ImageView = itemView.findViewById(R.id.list_icon)
|
||||||
var mainTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
var maxTempTV: TextView = itemView.findViewById(R.id.list_main_temp)
|
||||||
var minorTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
var minTempTV: TextView = itemView.findViewById(R.id.list_minor_temp)
|
||||||
|
var conditionTV: TextView = itemView.findViewById(R.id.list_condition)
|
||||||
|
|
||||||
fun bindView(forecast: Forecast?) {
|
fun bindView(forecast: Forecast?) {
|
||||||
dateTV.text = forecast?.date
|
dateTV.text = forecast?.date
|
||||||
dayTV.text = forecast?.day
|
dayTV.text = forecast?.day
|
||||||
conditionTV.text = forecast?.condition
|
|
||||||
weatherIV.loadImage(forecast?.weatherIcon)
|
weatherIV.loadImage(forecast?.weatherIcon)
|
||||||
mainTempTV.text = forecast?.mainTemp
|
maxTempTV.text = forecast?.mainTemp
|
||||||
minorTempTV.text = forecast?.minorTemp
|
minTempTV.text = forecast?.minorTemp
|
||||||
|
conditionTV.text = forecast?.condition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,20 +36,4 @@
|
|||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/progressBar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@android:color/black"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
@@ -43,17 +43,5 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||||
tools:layout="@layout/fragment_home" />
|
tools:layout="@layout/fragment_home" />
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress_circular"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:elevation="0.2dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/widget_item_layout"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="3dp"
|
||||||
|
android:layout_marginRight="3dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_item_day"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
tools:text="Dec 1" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/widget_item_image"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:contentDescription="@string/image_string"
|
||||||
|
tools:src="@drawable/cloud_symbol"
|
||||||
|
tools:tint="@color/colour_one" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/widget_item_temp_high"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="20" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/db_temp_unit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/degrees" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
24
app/src/atlasWeather/res/layout/hourly_item_forecast.xml
Normal file
24
app/src/atlasWeather/res/layout/hourly_item_forecast.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="24dp">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/forecast_recyclerview"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:spanCount="1"
|
||||||
|
tools:itemCount="24"
|
||||||
|
tools:listitem="@layout/widget_item" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -48,7 +48,6 @@ class MainViewModel(
|
|||||||
// Save data if not null
|
// Save data if not null
|
||||||
repository.saveLastSavedAt(CURRENT_LOCATION)
|
repository.saveLastSavedAt(CURRENT_LOCATION)
|
||||||
repository.saveCurrentWeatherToRoom(entityItem)
|
repository.saveCurrentWeatherToRoom(entityItem)
|
||||||
onSuccess(Unit)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
onError(e.message!!)
|
onError(e.message!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ class WorldViewModel(
|
|||||||
} else {
|
} else {
|
||||||
repository.getSingleWeather(locationName)
|
repository.getSingleWeather(locationName)
|
||||||
}
|
}
|
||||||
|
onSuccess(Unit)
|
||||||
repository.saveCurrentWeatherToRoom(weatherEntity)
|
repository.saveCurrentWeatherToRoom(weatherEntity)
|
||||||
repository.saveLastSavedAt(weatherEntity.id)
|
repository.saveLastSavedAt(weatherEntity.id)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
|||||||
@@ -15,6 +15,5 @@ class ViewHolderForecast(
|
|||||||
val adapter = GridForecastAdapter()
|
val adapter = GridForecastAdapter()
|
||||||
adapter.addCurrent(forecasts)
|
adapter.addCurrent(forecasts)
|
||||||
recyclerView.adapter = adapter
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,5 +35,5 @@ fun <T> LiveData<T>.getOrAwaitValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sleep(millis: Long = 1000) {
|
fun sleep(millis: Long = 1000) {
|
||||||
runBlocking(Dispatchers.IO) { delay(millis) }
|
runBlocking(Dispatchers.Default) { delay(millis) }
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@ import com.appttude.h_mal.atlas_weather.utils.sleep
|
|||||||
import io.mockk.MockKAnnotations
|
import io.mockk.MockKAnnotations
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
import io.mockk.impl.annotations.InjectMockKs
|
||||||
import io.mockk.impl.annotations.MockK
|
import io.mockk.impl.annotations.MockK
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@@ -28,6 +29,7 @@ class WorldViewModelTest : BaseTest() {
|
|||||||
@get:Rule
|
@get:Rule
|
||||||
val rule = InstantTaskExecutorRule()
|
val rule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
@InjectMockKs
|
||||||
lateinit var viewModel: WorldViewModel
|
lateinit var viewModel: WorldViewModel
|
||||||
|
|
||||||
@MockK(relaxed = true)
|
@MockK(relaxed = true)
|
||||||
@@ -36,12 +38,11 @@ class WorldViewModelTest : BaseTest() {
|
|||||||
@MockK
|
@MockK
|
||||||
lateinit var locationProvider: LocationProviderImpl
|
lateinit var locationProvider: LocationProviderImpl
|
||||||
|
|
||||||
lateinit var weatherResponse: WeatherResponse
|
private lateinit var weatherResponse: WeatherResponse
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockKAnnotations.init(this)
|
MockKAnnotations.init(this)
|
||||||
viewModel = WorldViewModel(locationProvider, repository)
|
|
||||||
|
|
||||||
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
|
weatherResponse = getTestData("weather_sample.json", WeatherResponse::class.java)
|
||||||
}
|
}
|
||||||
@@ -49,18 +50,17 @@ class WorldViewModelTest : BaseTest() {
|
|||||||
@Test
|
@Test
|
||||||
fun fetchDataForSingleLocation_validLocation_validReturn() {
|
fun fetchDataForSingleLocation_validLocation_validReturn() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val location = CURRENT_LOCATION
|
|
||||||
val entityItem = EntityItem(CURRENT_LOCATION, FullWeather(weatherResponse).apply {
|
val entityItem = EntityItem(CURRENT_LOCATION, FullWeather(weatherResponse).apply {
|
||||||
temperatureUnit = "°C"
|
temperatureUnit = "°C"
|
||||||
locationString = CURRENT_LOCATION
|
locationString = CURRENT_LOCATION
|
||||||
})
|
})
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
||||||
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns Pair(
|
coEvery { locationProvider.getLatLongFromLocationName(CURRENT_LOCATION) } returns Pair(
|
||||||
weatherResponse.lat,
|
weatherResponse.lat,
|
||||||
weatherResponse.lon
|
weatherResponse.lon
|
||||||
)
|
)
|
||||||
every { repository.isSearchValid(CURRENT_LOCATION) }.returns(true)
|
|
||||||
coEvery {
|
coEvery {
|
||||||
repository.getWeatherFromApi(
|
repository.getWeatherFromApi(
|
||||||
weatherResponse.lat.toString(),
|
weatherResponse.lat.toString(),
|
||||||
@@ -77,10 +77,14 @@ class WorldViewModelTest : BaseTest() {
|
|||||||
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit
|
every { repository.saveLastSavedAt(CURRENT_LOCATION) } returns Unit
|
||||||
coEvery { repository.saveCurrentWeatherToRoom(entityItem) } returns Unit
|
coEvery { repository.saveCurrentWeatherToRoom(entityItem) } returns Unit
|
||||||
|
|
||||||
viewModel.fetchDataForSingleLocation(location)
|
viewModel.fetchDataForSingleLocation(CURRENT_LOCATION)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
sleep(300)
|
viewModel.uiState.observeForever {
|
||||||
|
println(it.javaClass.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(3000)
|
||||||
assertIs<ViewState.HasData<*>>(viewModel.uiState.getOrAwaitValue())
|
assertIs<ViewState.HasData<*>>(viewModel.uiState.getOrAwaitValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user