initial commit (migrated)
This commit is contained in:
commit
b594facb51
143 changed files with 11057 additions and 0 deletions
4
test/_responses/_response.dart
Normal file
4
test/_responses/_response.dart
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import 'dart:io';
|
||||
|
||||
String readResponse(String name) =>
|
||||
File('test/_responses/$name.json').readAsStringSync();
|
||||
5
test/_responses/quote.json
Normal file
5
test/_responses/quote.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": 35499,
|
||||
"text": "I must have killed a lot of cows in a past life for Karma to hate me this much.",
|
||||
"author": "Katie McGarry, Pushing the Limits"
|
||||
}
|
||||
322
test/_responses/random_images.json
Normal file
322
test/_responses/random_images.json
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
[
|
||||
{
|
||||
"id": "hkZTIwcIvJg",
|
||||
"description": "A woman walking down a street in a white dress",
|
||||
"color": "#8c7373",
|
||||
"blurHash": "LFCrvE~BV@wIH=aJt6xaxaadWUoz",
|
||||
"url": "https://images.unsplash.com/photo-1725526750253-0abf4c3f4665?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-woman-walking-down-a-street-in-a-white-dress-hkZTIwcIvJg?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Raymond Petrik",
|
||||
"authorBio": "film photographer, music video director, travel lover, love through my pictures directly to all of you",
|
||||
"authorLocation": "Europe",
|
||||
"authorTotalLikes": 33,
|
||||
"authorTotalPhotos": 255,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1722151106527-1b1d892ef696?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@raymondpetrik?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "AU7WQdbQTdU",
|
||||
"description": "A black sports car parked in front of a building",
|
||||
"color": "#262626",
|
||||
"blurHash": "LgD]_3M{t7WV_NWBWCayWrofRjj[",
|
||||
"url": "https://images.unsplash.com/photo-1721355623034-807a3251163a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-black-sports-car-parked-in-front-of-a-building-AU7WQdbQTdU?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Erik Mclean",
|
||||
"authorBio": "Donations are welcome! paypal.me/Introspectivedsgn \r\n& Feel free to reach out if you wish to purchase selling rights. Give me a follow on instagram @introspectivedsgn",
|
||||
"authorLocation": "st. Johns, NL",
|
||||
"authorTotalLikes": 311,
|
||||
"authorTotalPhotos": 17700,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1605586339247-f9d24f56b74eimage?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@introspectivedsgn?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "ySZNyM6-q_Q",
|
||||
"description": "A small white building with a clock tower on top of it",
|
||||
"color": "#f3f3f3",
|
||||
"blurHash": "LoMtHrRkt7t7_Nt7ofM{_3ofayax",
|
||||
"url": "https://images.unsplash.com/photo-1723479319633-43fa297d3853?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-small-white-building-with-a-clock-tower-on-top-of-it-ySZNyM6-q_Q?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "oo verthing",
|
||||
"authorBio": "SWEETHEART AROUND YOU.",
|
||||
"authorLocation": "郑州 zhengzhou",
|
||||
"authorTotalLikes": 6006,
|
||||
"authorTotalPhotos": 798,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1671458629285-b7bfa964e570image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@ooverthing?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "iUx8Ovld4UI",
|
||||
"description": "A person standing on a beach near the ocean",
|
||||
"color": "#595940",
|
||||
"blurHash": "LmG*+pIoWBaz~VRjWBfRx]axWBj[",
|
||||
"url": "https://images.unsplash.com/photo-1723619884726-f246de4dae07?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-person-standing-on-a-beach-near-the-ocean-iUx8Ovld4UI?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Shiebi AL",
|
||||
"authorBio": "Fotógrafa,México.",
|
||||
"authorLocation": "México",
|
||||
"authorTotalLikes": 127,
|
||||
"authorTotalPhotos": 61,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1725483661358-c6fa0d2d8ad5image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@shiebi?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "VS6jRcrwZhA",
|
||||
"description": "The sun is setting over the water in the distance",
|
||||
"color": "#595959",
|
||||
"blurHash": "LMC$o.M|I;jZ~UR*Nbj@T0jZs,az",
|
||||
"url": "https://images.unsplash.com/photo-1725402346958-ea04d6b7b657?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/the-sun-is-setting-over-the-water-in-the-distance-VS6jRcrwZhA?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Phil Botha",
|
||||
"authorBio": "Aotearoa, New Zealand",
|
||||
"authorLocation": "New Zealand",
|
||||
"authorTotalLikes": 17,
|
||||
"authorTotalPhotos": 52,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1618470173660-888bde2e3181image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@philbotha?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "LD0NgEgTMuQ",
|
||||
"description": "A jellyfish swimming in the water at night",
|
||||
"color": "#595959",
|
||||
"blurHash": "L44V8rWC4mj[W?oft6ax00of?bf6",
|
||||
"url": "https://images.unsplash.com/photo-1723581048670-bdd958641c2e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-jellyfish-swimming-in-the-water-at-night-LD0NgEgTMuQ?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Zhen Yao",
|
||||
"authorBio": "Try to capture the beautiful moments in my life",
|
||||
"authorLocation": "Pittsburgh, PA",
|
||||
"authorTotalLikes": 1105,
|
||||
"authorTotalPhotos": 626,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1545600588648-05099ce74acf?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@zhenyao_photo?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "_O-f6C11iys",
|
||||
"description": "A woman with red hair is walking in front of a black wall",
|
||||
"color": "#262626",
|
||||
"blurHash": "L87w$v%M4:R*0Kof-:Rj.8kDWBt7",
|
||||
"url": "https://images.unsplash.com/photo-1725221904479-9b196e4b0d67?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-woman-with-red-hair-is-walking-in-front-of-a-black-wall-_O-f6C11iys?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Pavel Moiseev",
|
||||
"authorBio": "No bio",
|
||||
"authorLocation": "Mülheim an der Ruhr, Germany",
|
||||
"authorTotalLikes": 22,
|
||||
"authorTotalPhotos": 60,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1713706650525-167e671fbd83image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@pavelmois?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "uOE2ttzJVhg",
|
||||
"description": "A woman with her eyes closed standing in a room",
|
||||
"color": "#c0c0c0",
|
||||
"blurHash": "LOKUZh00S4.8.8%g%M%M_4-:RPIU",
|
||||
"url": "https://images.unsplash.com/photo-1724086575243-6796fc662673?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-woman-with-her-eyes-closed-standing-in-a-room-uOE2ttzJVhg?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Alexander Mass",
|
||||
"authorBio": "No bio",
|
||||
"authorLocation": "Unknown",
|
||||
"authorTotalLikes": 0,
|
||||
"authorTotalPhotos": 1660,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1702352055453-324fc6a5e3cbimage?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@alexandermassph?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "azIP-LUvIXs",
|
||||
"description": "A red building sitting on the side of a body of water",
|
||||
"color": "#d9d9d9",
|
||||
"blurHash": "LYG[sLDhM{kV~qRjRjWByXRjjFn%",
|
||||
"url": "https://images.unsplash.com/photo-1724271361924-ad07d11d4add?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-red-building-sitting-on-the-side-of-a-body-of-water-azIP-LUvIXs?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Daniel Seßler",
|
||||
"authorBio": "Thank you for visting my profile!\r\nIf you want to support me creating more photos for Unsplash you can help me with a small donation. But a thank you is enough as well 😊",
|
||||
"authorLocation": "Munich",
|
||||
"authorTotalLikes": 1009,
|
||||
"authorTotalPhotos": 546,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1634653553021-5ee00c501272image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@danielsessler?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "RUJErP3X0fY",
|
||||
"description": "An ornate doorway with a large metal door",
|
||||
"color": "#c0a68c",
|
||||
"blurHash": "LOIz-JxZ^%WX~VofEMj[9voeRjj?",
|
||||
"url": "https://images.unsplash.com/photo-1724762115854-01798ad66178?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/an-ornate-doorway-with-a-large-metal-door-RUJErP3X0fY?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Jimmy Woo",
|
||||
"authorBio": "Culturist\r\n鍾意睇書同遠方 影低人類世",
|
||||
"authorLocation": "Americas | Asia",
|
||||
"authorTotalLikes": 251,
|
||||
"authorTotalPhotos": 1162,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1695577181301-19005b97e484image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@woomantsing?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "Msc61p_9BwA",
|
||||
"description": "An electric guitar sitting on top of a table",
|
||||
"color": "#26260c",
|
||||
"blurHash": "LP9*P,aJD%of8^azxut7x^oLaeR*",
|
||||
"url": "https://images.unsplash.com/photo-1723714807771-23b5447e2f65?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/an-electric-guitar-sitting-on-top-of-a-table-Msc61p_9BwA?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Oleg Brovchenko",
|
||||
"authorBio": "I'm Oleg Brovchenko, a music video director, videographer, based in Spain. With over 10 years of experience. Founder of the Full Drill Production. I've collaborated with top artists, creating viral multi-platinum videos across the country.",
|
||||
"authorLocation": "Madrid",
|
||||
"authorTotalLikes": 0,
|
||||
"authorTotalPhotos": 107,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1708191223327-80acb220d68dimage?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@olegbrovchenko?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "qVkbq2obFyo",
|
||||
"description": "A dark blue background with lots of leaves",
|
||||
"color": "#0c0c26",
|
||||
"blurHash": "L6003^fUfHfNfVfRfRfRfHfMfWfS",
|
||||
"url": "https://images.unsplash.com/photo-1724995922268-cec4bb414b86?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-dark-blue-background-with-lots-of-leaves-qVkbq2obFyo?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Eugene Golovesov",
|
||||
"authorBio": "Hello everyone!\r\nWelcome to my profile. Here I share my photos. You can see even more of my photos on my Instagram: @eugenegolovesov. My Behance: @eugenegolovesov. Thank you for your attention! 🙏",
|
||||
"authorLocation": "Unknown",
|
||||
"authorTotalLikes": 47558,
|
||||
"authorTotalPhotos": 1226,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1704991443592-a7f79d25ffb1image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@eugene_golovesov?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "GXgqi2etim0",
|
||||
"description": "A steering wheel and dashboard of a car",
|
||||
"color": "#d9d9d9",
|
||||
"blurHash": "LoH2ZgM_WBj[~qIUWBofM|M{M{fk",
|
||||
"url": "https://images.unsplash.com/photo-1725127077038-3aa8c931645d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-steering-wheel-and-dashboard-of-a-car-GXgqi2etim0?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Ricardo Resende",
|
||||
"authorBio": "Hobbyist photographer from Portugal. \r\nLooking for Work in Aveiro, Portugal :) If you'd like to support me, you can consider a donation paypal.me/Rresenden",
|
||||
"authorLocation": "Portugal",
|
||||
"authorTotalLikes": 28,
|
||||
"authorTotalPhotos": 763,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1627912212709-c3120c50c449image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@rresenden?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "xjrv4fKWsNI",
|
||||
"description": "A bottle of tic skin oil sitting on top of a tree branch",
|
||||
"color": "#40260c",
|
||||
"blurHash": "L79~m1%1E29u-UozNHV@10a}W=oK",
|
||||
"url": "https://images.unsplash.com/photo-1723391962208-b5c92f400816?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-bottle-of-tic-skin-oil-sitting-on-top-of-a-tree-branch-xjrv4fKWsNI?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Pavlo T",
|
||||
"authorBio": "No bio",
|
||||
"authorLocation": "Ukraine",
|
||||
"authorTotalLikes": 312,
|
||||
"authorTotalPhotos": 61,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1673344928632-5fa23ed8d94bimage?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@pavlo_talpa?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "pHC1igR4Cns",
|
||||
"description": "A person sitting at a table with a bunch of books",
|
||||
"color": "#d9c0c0",
|
||||
"blurHash": "LDI}Fgx]0KI:~qEM9F9GMdxb-o-:",
|
||||
"url": "https://images.unsplash.com/photo-1724963578391-dcf77410bb73?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-person-sitting-at-a-table-with-a-bunch-of-books-pHC1igR4Cns?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Toa Heftiba",
|
||||
"authorBio": "ᴘʀᴏᴅᴜᴄᴛ | ꜰᴏᴏᴅ | ʟɪꜰᴇꜱᴛʏʟᴇ ᴘʜᴏᴛᴏɢʀᴀᴘʜᴇʀ • ᴀ ꜰᴀɴ ᴏꜰ ᴏᴅᴅ ᴛʜɪɴɢꜱ ᴀɴᴅ ɢᴏᴏᴅ ʜᴜᴍᴏᴜʀ, –a hopeless romantic with karma on her back🥹\r\n👉🏻ᴄʟɪᴄᴋ ᴛʜᴇ 'ᴄᴏʟʟᴇᴄᴛɪᴏɴꜱ' ᴛᴀʙ ʙᴇʟᴏᴡ ᴛᴏ ᴠɪᴇᴡ ᴍʏ ɪᴍᴀɢᴇꜱ ɪɴ ᴏʀɢᴀɴɪꜱᴇᴅ ꜰᴏʟᴅᴇʀꜱ. ᴡʜʏ ɴᴏᴛ ꜱᴀʏ ʜᴇʟʟᴏ ⚡️ ɪɢ: @ʜᴇꜰᴛɪʙᴀ.ᴄᴏ.ᴜᴋ",
|
||||
"authorLocation": "UK",
|
||||
"authorTotalLikes": 2442,
|
||||
"authorTotalPhotos": 3931,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1605780274397-200ea3604d6fimage?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@heftiba?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "nOIjHjsJrKo",
|
||||
"description": "A woman standing in front of a green screen",
|
||||
"color": "#0c8c26",
|
||||
"blurHash": "LM9dFX8yIB-q54?bo|In+*Inw~oe",
|
||||
"url": "https://images.unsplash.com/photo-1724812569788-ccac27cf530a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-woman-standing-in-front-of-a-green-screen-nOIjHjsJrKo?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Dwayne joe",
|
||||
"authorBio": "Love taking Portraits.",
|
||||
"authorLocation": "Kenya",
|
||||
"authorTotalLikes": 123,
|
||||
"authorTotalPhotos": 458,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1651706213170-8be43fd00ae7?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@spliff_dj_joe?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "wpMCTmxivtk",
|
||||
"description": "The sun is setting over a body of water",
|
||||
"color": "#262626",
|
||||
"blurHash": "LXDarc9^S3s.}rI=WWoK$%ayazWV",
|
||||
"url": "https://images.unsplash.com/photo-1725827866746-21021ebabcfc?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/the-sun-is-setting-over-a-body-of-water-wpMCTmxivtk?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Alvin Mahmudov",
|
||||
"authorBio": "No bio",
|
||||
"authorLocation": "Azerbaijan Baku",
|
||||
"authorTotalLikes": 42,
|
||||
"authorTotalPhotos": 65,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1658572107488-64663405b6f0image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@alvinmahmudov?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "KjkZqlBayFc",
|
||||
"description": "A living room filled with furniture and a large window",
|
||||
"color": "#a6a68c",
|
||||
"blurHash": "LIIEkL-;jEae~pIUoy%1MdITRkIp",
|
||||
"url": "https://images.unsplash.com/photo-1724775255163-b0f5f5241642?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-living-room-filled-with-furniture-and-a-large-window-KjkZqlBayFc?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Clay Banks",
|
||||
"authorBio": "📷 Freelance Photog\r\n📍 Catskill Mountains NY 🌎 Presets & Prints 👉🏽 https://claybanks.info If you use my images and would like to say thanks, feel free to donate via the PayPal link on my profile",
|
||||
"authorLocation": "New York",
|
||||
"authorTotalLikes": 517,
|
||||
"authorTotalPhotos": 1338,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1670236743900-356b1ee0dc42image?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@claybanks?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "CFvc4aFsWOM",
|
||||
"description": "A view of a city with tall buildings",
|
||||
"color": "#73c0d9",
|
||||
"blurHash": "L:HCl;oft6a}ysoLa|fk9wayR*j[",
|
||||
"url": "https://images.unsplash.com/photo-1710092489927-8f382249cc51?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-view-of-a-city-with-tall-buildings-CFvc4aFsWOM?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Zhen Yao",
|
||||
"authorBio": "Try to capture the beautiful moments in my life",
|
||||
"authorLocation": "Pittsburgh, PA",
|
||||
"authorTotalLikes": 1105,
|
||||
"authorTotalPhotos": 626,
|
||||
"authorIsForHire": false,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1545600588648-05099ce74acf?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@zhenyao_photo?utm_source=kuwot&utm_medium=referral"
|
||||
},
|
||||
{
|
||||
"id": "kwV8FbXF7Bo",
|
||||
"description": "A man standing in front of a white bus",
|
||||
"color": "#d9d9d9",
|
||||
"blurHash": "L#HoOGM{ofWB~qWBazjtx]ofWBj@",
|
||||
"url": "https://images.unsplash.com/photo-1722218531378-db77ec2eea7f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzNTU1MTd8MHwxfHJhbmRvbXx8fHx8fHx8fDE3MjYxMjcxMzV8&ixlib=rb-4.0.3&q=80&w=1080",
|
||||
"originUrl": "https://unsplash.com/photos/a-man-standing-in-front-of-a-white-bus-kwV8FbXF7Bo?utm_source=kuwot&utm_medium=referral",
|
||||
"authorName": "Jose Figueroa",
|
||||
"authorBio": "No bio",
|
||||
"authorLocation": "Unknown",
|
||||
"authorTotalLikes": 11,
|
||||
"authorTotalPhotos": 187,
|
||||
"authorIsForHire": true,
|
||||
"authorProfileImageUrl": "https://images.unsplash.com/profile-1675085333943-8cd12a953307?ixlib=rb-4.0.3&crop=faces&fit=crop&w=128&h=128",
|
||||
"authorUrl": "https://unsplash.com/@josefigueroa_mov?utm_source=kuwot&utm_medium=referral"
|
||||
}
|
||||
]
|
||||
52
test/_responses/translations.json
Normal file
52
test/_responses/translations.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
[
|
||||
{
|
||||
"id": "en",
|
||||
"lang": "English",
|
||||
"tableName": "quotes"
|
||||
},
|
||||
{
|
||||
"id": "id",
|
||||
"lang": "Indonesian",
|
||||
"tableName": "quotes_id"
|
||||
},
|
||||
{
|
||||
"id": "es",
|
||||
"lang": "Spanish",
|
||||
"tableName": "quotes_es"
|
||||
},
|
||||
{
|
||||
"id": "de",
|
||||
"lang": "Dutch",
|
||||
"tableName": "quotes_de"
|
||||
},
|
||||
{
|
||||
"id": "fr",
|
||||
"lang": "French",
|
||||
"tableName": "quotes_fr"
|
||||
},
|
||||
{
|
||||
"id": "hi",
|
||||
"lang": "Hindi",
|
||||
"tableName": "quotes_hi"
|
||||
},
|
||||
{
|
||||
"id": "it",
|
||||
"lang": "Italian",
|
||||
"tableName": "quotes_it"
|
||||
},
|
||||
{
|
||||
"id": "ru",
|
||||
"lang": "Russian",
|
||||
"tableName": "quotes_ru"
|
||||
},
|
||||
{
|
||||
"id": "zh-CN",
|
||||
"lang": "Chinese (Simplified)",
|
||||
"tableName": "quotes_zhcn"
|
||||
},
|
||||
{
|
||||
"id": "ja",
|
||||
"lang": "Japanese",
|
||||
"tableName": "quotes_ja"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:in_app_update/in_app_update.dart';
|
||||
import 'package:kuwot/core/app_updater.dart';
|
||||
import 'package:kuwot/features/in_app_update/presentation/bloc/in_app_update_bloc.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockAppUpdater extends Mock implements AppUpdater {}
|
||||
|
||||
void main() {
|
||||
late MockAppUpdater mockAppUpdater;
|
||||
late InAppUpdateBloc bloc;
|
||||
|
||||
setUp(() {
|
||||
mockAppUpdater = MockAppUpdater();
|
||||
bloc = InAppUpdateBloc(appUpdater: mockAppUpdater);
|
||||
});
|
||||
|
||||
test('initial state should be InAppUpdateInitialState', () {
|
||||
// assert
|
||||
expect(bloc.state, const InAppUpdateInitialState());
|
||||
});
|
||||
|
||||
group('InAppUpdateCheck', () {
|
||||
test(
|
||||
'should emit [Check, UpdateAvailable] when update is available',
|
||||
() async {
|
||||
// arrange
|
||||
final tAppUpdateInfo = AppUpdateInfo(
|
||||
updateAvailability: UpdateAvailability.updateAvailable,
|
||||
immediateUpdateAllowed: true,
|
||||
immediateAllowedPreconditions: [],
|
||||
flexibleUpdateAllowed: true,
|
||||
flexibleAllowedPreconditions: [],
|
||||
availableVersionCode: 1,
|
||||
installStatus: InstallStatus.pending,
|
||||
packageName: 'com.example.app',
|
||||
clientVersionStalenessDays: 1,
|
||||
updatePriority: 1,
|
||||
);
|
||||
when(
|
||||
() => mockAppUpdater.checkForUpdate(),
|
||||
).thenAnswer((_) async => tAppUpdateInfo);
|
||||
|
||||
// expect later
|
||||
final expected = [
|
||||
const InAppUpdateCheckingState(),
|
||||
const InAppUpdateAvailableState(),
|
||||
];
|
||||
expectLater(bloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
bloc.add(const InAppUpdateCheckEvent());
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should emit [Check, UpdateUnavailable] when update is not available',
|
||||
() async {
|
||||
// arrange
|
||||
final tAppUpdateInfo = AppUpdateInfo(
|
||||
updateAvailability: UpdateAvailability.updateNotAvailable,
|
||||
immediateUpdateAllowed: true,
|
||||
immediateAllowedPreconditions: [],
|
||||
flexibleUpdateAllowed: true,
|
||||
flexibleAllowedPreconditions: [],
|
||||
availableVersionCode: 1,
|
||||
installStatus: InstallStatus.pending,
|
||||
packageName: 'com.example.app',
|
||||
clientVersionStalenessDays: 1,
|
||||
updatePriority: 1,
|
||||
);
|
||||
when(
|
||||
() => mockAppUpdater.checkForUpdate(),
|
||||
).thenAnswer((_) async => tAppUpdateInfo);
|
||||
|
||||
// expect later
|
||||
final expected = [
|
||||
const InAppUpdateCheckingState(),
|
||||
const InAppUpdateUnavailableState(),
|
||||
];
|
||||
expectLater(bloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
bloc.add(const InAppUpdateCheckEvent());
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('InAppUpdateStart', () {});
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:kuwot/core/env.dart';
|
||||
import 'package:kuwot/core/network/network.dart';
|
||||
import 'package:kuwot/features/quote/data/data_sources/remote/kuwot_api_remote_data_source.dart';
|
||||
import 'package:kuwot/features/quote/data/models/image_model.dart';
|
||||
import 'package:kuwot/features/quote/data/models/quote_model.dart';
|
||||
import 'package:kuwot/features/quote/data/models/translation_model.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../../../../_responses/_response.dart';
|
||||
|
||||
class MockEnv extends Mock implements Env {}
|
||||
|
||||
class MockNetwork extends Mock implements Network {}
|
||||
|
||||
class FakeUri extends Fake implements Uri {}
|
||||
|
||||
void main() {
|
||||
late MockEnv mockEnv;
|
||||
late MockNetwork mockNetwork;
|
||||
late KuwotApiRemoteDataSource dataSource;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeUri());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockEnv = MockEnv();
|
||||
mockNetwork = MockNetwork();
|
||||
dataSource = KuwotApiRemoteApiImpl(env: mockEnv, network: mockNetwork);
|
||||
|
||||
// global stubs
|
||||
when(() => mockEnv.authPublicKey).thenReturn('test');
|
||||
when(() => mockEnv.quoteApiScheme).thenReturn('http');
|
||||
when(() => mockEnv.quoteApiHost).thenReturn('10.0.2.2');
|
||||
when(() => mockEnv.quoteApiPort).thenReturn(8080);
|
||||
});
|
||||
|
||||
group('getDailyQuote', () {
|
||||
test('should return random quote when response is successful', () async {
|
||||
// arrange
|
||||
final tResponse = readResponse('quote');
|
||||
when(
|
||||
() => mockNetwork.get(any(), headers: any(named: 'headers')),
|
||||
).thenAnswer((_) async => tResponse);
|
||||
// act
|
||||
final result = await dataSource.getQuote();
|
||||
// assert
|
||||
expect(result, isA<QuoteModel>());
|
||||
});
|
||||
|
||||
test(
|
||||
'should return translated quote when response is successful',
|
||||
() async {
|
||||
// arrange
|
||||
final tResponse = readResponse('quote');
|
||||
when(
|
||||
() => mockNetwork.get(any(), headers: any(named: 'headers')),
|
||||
).thenAnswer((_) async => tResponse);
|
||||
// act
|
||||
final result = await dataSource.getTranslatedQuote(1);
|
||||
// assert
|
||||
expect(result, isA<QuoteModel>());
|
||||
},
|
||||
);
|
||||
|
||||
test('should return random images when response is successful', () async {
|
||||
// arrange
|
||||
final tResponse = readResponse('random_images');
|
||||
when(
|
||||
() => mockNetwork.get(any(), headers: any(named: 'headers')),
|
||||
).thenAnswer((_) async => tResponse);
|
||||
// act
|
||||
final result = await dataSource.getRandomImages();
|
||||
// assert
|
||||
expect(result, isA<List<ImageModel>>());
|
||||
});
|
||||
|
||||
test('should return translations when response is successful', () async {
|
||||
// arrange
|
||||
final tResponse = readResponse('translations');
|
||||
when(
|
||||
() => mockNetwork.get(any(), headers: any(named: 'headers')),
|
||||
).thenAnswer((_) async => tResponse);
|
||||
// act
|
||||
final result = await dataSource.getTranslations();
|
||||
// assert
|
||||
expect(result, isA<List<TranslationModel>>());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:kuwot/core/data/local/translation_target_config.dart';
|
||||
import 'package:kuwot/core/error/failure.dart';
|
||||
import 'package:kuwot/features/quote/data/data_sources/remote/kuwot_api_remote_data_source.dart';
|
||||
import 'package:kuwot/features/quote/data/models/image_model.dart';
|
||||
import 'package:kuwot/features/quote/data/models/quote_model.dart';
|
||||
import 'package:kuwot/features/quote/data/models/translation_model.dart';
|
||||
import 'package:kuwot/features/quote/data/repositories/quote_repository_impl.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/background_image.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/quote.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/translation.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
import '../../../../_responses/_response.dart';
|
||||
|
||||
class MockKuwotApiRemoteDataSource extends Mock
|
||||
implements KuwotApiRemoteDataSource {}
|
||||
|
||||
void main() {
|
||||
late QuoteRepositoryImpl quoteRepository;
|
||||
late MockKuwotApiRemoteDataSource mockKuwotApiRemoteDataSource;
|
||||
|
||||
setUp(() {
|
||||
mockKuwotApiRemoteDataSource = MockKuwotApiRemoteDataSource();
|
||||
|
||||
quoteRepository = QuoteRepositoryImpl(
|
||||
quoteDataSource: mockKuwotApiRemoteDataSource,
|
||||
);
|
||||
});
|
||||
|
||||
const tQuoteModel = QuoteModel(id: 1, author: 'author', text: 'text');
|
||||
const tExpectedQuote = Quote(id: 1, author: 'author', body: 'text');
|
||||
const tTranslationTarget = TranslationTarget(id: 'en', name: 'English');
|
||||
|
||||
group('getQuote', () {
|
||||
test('should return a Quote entity', () async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getQuote(query: any(named: 'query')),
|
||||
).thenAnswer((_) async => tQuoteModel);
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getQuote(tTranslationTarget);
|
||||
|
||||
// assert
|
||||
verify(
|
||||
() => mockKuwotApiRemoteDataSource.getQuote(query: any(named: 'query')),
|
||||
);
|
||||
result.fold(
|
||||
(failure) => fail('Expected Quote, but got $failure'),
|
||||
(quote) => expect(quote, tExpectedQuote),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return ClientFailure when a client exception is thrown',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() =>
|
||||
mockKuwotApiRemoteDataSource.getQuote(query: any(named: 'query')),
|
||||
).thenThrow(ClientException('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getQuote(null);
|
||||
|
||||
// assert
|
||||
verify(
|
||||
() =>
|
||||
mockKuwotApiRemoteDataSource.getQuote(query: any(named: 'query')),
|
||||
);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ClientFailure>()),
|
||||
(quote) => fail('Expected ClientFailure, but got $quote'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
},
|
||||
);
|
||||
|
||||
test('should return UnknownFailure when an exception is thrown', () async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getQuote(query: any(named: 'query')),
|
||||
).thenThrow(Exception('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getQuote(null);
|
||||
|
||||
// assert
|
||||
verify(
|
||||
() => mockKuwotApiRemoteDataSource.getQuote(query: any(named: 'query')),
|
||||
);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<UnknownFailure>()),
|
||||
(quote) => fail('Expected UnknownFailure, but got $quote'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
});
|
||||
|
||||
group('getTranslatedQuote', () {
|
||||
const tQuoteId = 1;
|
||||
|
||||
test('should return a Quote entity', () async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslatedQuote(
|
||||
any(),
|
||||
query: any(named: 'query'),
|
||||
),
|
||||
).thenAnswer((_) async => tQuoteModel);
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getTranslatedQuote(
|
||||
tQuoteId,
|
||||
tTranslationTarget,
|
||||
);
|
||||
|
||||
// assert
|
||||
verify(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslatedQuote(
|
||||
any(),
|
||||
query: any(named: 'query'),
|
||||
),
|
||||
);
|
||||
result.fold(
|
||||
(failure) => fail('Expected Quote, but got $failure'),
|
||||
(quote) => expect(quote, tExpectedQuote),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return ClientFailure when a client exception is thrown',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslatedQuote(
|
||||
any(),
|
||||
query: any(named: 'query'),
|
||||
),
|
||||
).thenThrow(ClientException('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getTranslatedQuote(
|
||||
tQuoteId,
|
||||
tTranslationTarget,
|
||||
);
|
||||
|
||||
// assert
|
||||
verify(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslatedQuote(
|
||||
any(),
|
||||
query: any(named: 'query'),
|
||||
),
|
||||
);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ClientFailure>()),
|
||||
(quote) => fail('Expected ClientFailure, but got $quote'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
},
|
||||
);
|
||||
|
||||
test('should return UnknownFailure when an exception is thrown', () async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslatedQuote(
|
||||
any(),
|
||||
query: any(named: 'query'),
|
||||
),
|
||||
).thenThrow(Exception('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getTranslatedQuote(
|
||||
tQuoteId,
|
||||
tTranslationTarget,
|
||||
);
|
||||
|
||||
// assert
|
||||
verify(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslatedQuote(
|
||||
any(),
|
||||
query: any(named: 'query'),
|
||||
),
|
||||
);
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<UnknownFailure>()),
|
||||
(quote) => fail('Expected UnknownFailure, but got $quote'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
});
|
||||
|
||||
group('getTranslations', () {
|
||||
test('should return a List of Translation entity', () async {
|
||||
// arrange
|
||||
final tTranslationListModel =
|
||||
(jsonDecode(readResponse('translations')) as List)
|
||||
.map((e) => TranslationModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
final tExpectedTranslations = tTranslationListModel
|
||||
.map((e) => Translation(id: e.id, language: e.lang))
|
||||
.toList();
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslations(),
|
||||
).thenAnswer((_) async => tTranslationListModel);
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getTranslations();
|
||||
|
||||
// assert
|
||||
verify(() => mockKuwotApiRemoteDataSource.getTranslations());
|
||||
result.fold(
|
||||
(failure) => fail('Expected Translations, but got $failure'),
|
||||
(translations) => expect(translations, tExpectedTranslations),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return ClientFailure when a client exception is thrown',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslations(),
|
||||
).thenThrow(ClientException('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getTranslations();
|
||||
|
||||
// assert
|
||||
verify(() => mockKuwotApiRemoteDataSource.getTranslations());
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ClientFailure>()),
|
||||
(translations) =>
|
||||
fail('Expected ClientFailure, but got $translations'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
},
|
||||
);
|
||||
|
||||
test('should return UnknownFailure when an exception is thrown', () async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getTranslations(),
|
||||
).thenThrow(Exception('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getTranslations();
|
||||
|
||||
// assert
|
||||
verify(() => mockKuwotApiRemoteDataSource.getTranslations());
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<UnknownFailure>()),
|
||||
(translations) =>
|
||||
fail('Expected UnknownFailure, but got $translations'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
});
|
||||
|
||||
group('getBackgroundImages', () {
|
||||
test('should return a List of BackgroundImage entity', () async {
|
||||
// arrange
|
||||
final tImageListModel =
|
||||
(jsonDecode(readResponse('random_images')) as List)
|
||||
.map((e) => ImageModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
final tExpectedImages = tImageListModel.map(
|
||||
(e) => BackgroundImage(
|
||||
id: e.id,
|
||||
description: e.description,
|
||||
color: e.color,
|
||||
blurHash: e.blurHash,
|
||||
url: e.url,
|
||||
originUrl: e.originUrl,
|
||||
authorName: e.authorName,
|
||||
authorProfileImageUrl: e.authorProfileImageUrl,
|
||||
authorUrl: e.authorUrl,
|
||||
authorBio: e.authorBio,
|
||||
authorLocation: e.authorLocation,
|
||||
authorTotalLikes: e.authorTotalLikes,
|
||||
authorTotalPhotos: e.authorTotalPhotos,
|
||||
authorIsForHire: e.authorIsForHire,
|
||||
),
|
||||
);
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getRandomImages(),
|
||||
).thenAnswer((_) async => tImageListModel);
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getBackgroundImages();
|
||||
|
||||
// assert
|
||||
verify(() => mockKuwotApiRemoteDataSource.getRandomImages());
|
||||
result.fold(
|
||||
(failure) => fail('Expected BackgroundImages, but got $failure'),
|
||||
(photos) => expect(photos, tExpectedImages),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
|
||||
test(
|
||||
'should return ClientFailure when a client exception is thrown',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getRandomImages(),
|
||||
).thenThrow(ClientException('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getBackgroundImages();
|
||||
|
||||
// assert
|
||||
verify(() => mockKuwotApiRemoteDataSource.getRandomImages());
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<ClientFailure>()),
|
||||
(images) => fail('Expected ClientFailure, but got $images'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
},
|
||||
);
|
||||
|
||||
test('should return UnknownFailure when an exception is thrown', () async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockKuwotApiRemoteDataSource.getRandomImages(),
|
||||
).thenThrow(Exception('test'));
|
||||
|
||||
// act
|
||||
final result = await quoteRepository.getBackgroundImages();
|
||||
|
||||
// assert
|
||||
verify(() => mockKuwotApiRemoteDataSource.getRandomImages());
|
||||
result.fold(
|
||||
(failure) => expect(failure, isA<UnknownFailure>()),
|
||||
(images) => fail('Expected UnknownFailure, but got $images'),
|
||||
);
|
||||
verifyNoMoreInteractions(mockKuwotApiRemoteDataSource);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/core/domain/no_params.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/background_image.dart';
|
||||
import 'package:kuwot/features/quote/domain/repositories/quote_repository.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_background_images.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockQuoteRepository extends Mock implements QuoteRepository {}
|
||||
|
||||
void main() {
|
||||
late MockQuoteRepository mockQuoteRepository;
|
||||
late GetBackgroundImages useCase;
|
||||
|
||||
setUp(() {
|
||||
mockQuoteRepository = MockQuoteRepository();
|
||||
useCase = GetBackgroundImages(mockQuoteRepository);
|
||||
});
|
||||
|
||||
test('should get background photos', () async {
|
||||
// arrange
|
||||
const tImages = <BackgroundImage>[];
|
||||
when(
|
||||
() => mockQuoteRepository.getBackgroundImages(),
|
||||
).thenAnswer((_) async => right(tImages));
|
||||
|
||||
// act
|
||||
final result = await useCase(const NoParams());
|
||||
|
||||
// assert
|
||||
expect(result, right(tImages));
|
||||
verify(() => mockQuoteRepository.getBackgroundImages());
|
||||
verifyNoMoreInteractions(mockQuoteRepository);
|
||||
});
|
||||
}
|
||||
34
test/features/quote/domain/use_cases/get_quote_test.dart
Normal file
34
test/features/quote/domain/use_cases/get_quote_test.dart
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/quote.dart';
|
||||
import 'package:kuwot/features/quote/domain/repositories/quote_repository.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_quote.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockQuoteRepository extends Mock implements QuoteRepository {}
|
||||
|
||||
void main() {
|
||||
late MockQuoteRepository mockQuoteRepository;
|
||||
late GetQuote useCase;
|
||||
|
||||
setUp(() {
|
||||
mockQuoteRepository = MockQuoteRepository();
|
||||
useCase = GetQuote(mockQuoteRepository);
|
||||
});
|
||||
|
||||
test('should get quote', () async {
|
||||
// arrange
|
||||
const tQuote = Quote(id: 1, author: 'author', body: 'text');
|
||||
when(
|
||||
() => mockQuoteRepository.getQuote(any()),
|
||||
).thenAnswer((_) async => right(tQuote));
|
||||
|
||||
// act
|
||||
final result = await useCase(const GetQuoteParams(null));
|
||||
|
||||
// assert
|
||||
expect(result, right(tQuote));
|
||||
verify(() => mockQuoteRepository.getQuote(any()));
|
||||
verifyNoMoreInteractions(mockQuoteRepository);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/core/data/local/translation_target_config.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/quote.dart';
|
||||
import 'package:kuwot/features/quote/domain/repositories/quote_repository.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_translated_quote.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockQuoteRepository extends Mock implements QuoteRepository {}
|
||||
|
||||
class FakeTranslationTarget extends Fake implements TranslationTarget {}
|
||||
|
||||
void main() {
|
||||
late MockQuoteRepository mockQuoteRepository;
|
||||
late GetTranslatedQuote useCase;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeTranslationTarget());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockQuoteRepository = MockQuoteRepository();
|
||||
useCase = GetTranslatedQuote(mockQuoteRepository);
|
||||
});
|
||||
|
||||
test('should get translated quote', () async {
|
||||
// arrange
|
||||
const tQuote = Quote(id: 1, author: 'author', body: 'text');
|
||||
const tTarget = TranslationTarget(id: 'en', name: 'English');
|
||||
when(
|
||||
() => mockQuoteRepository.getTranslatedQuote(any(), any()),
|
||||
).thenAnswer((_) async => right(tQuote));
|
||||
|
||||
// act
|
||||
final result = await useCase(
|
||||
const GetTranslatedQuoteParams(id: 1, translationTarget: tTarget),
|
||||
);
|
||||
|
||||
// assert
|
||||
expect(result, right(tQuote));
|
||||
verify(() => mockQuoteRepository.getTranslatedQuote(tQuote.id, tTarget));
|
||||
verifyNoMoreInteractions(mockQuoteRepository);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/core/domain/no_params.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/translation.dart';
|
||||
import 'package:kuwot/features/quote/domain/repositories/quote_repository.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_translations.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockQuoteRepository extends Mock implements QuoteRepository {}
|
||||
|
||||
void main() {
|
||||
late MockQuoteRepository mockQuoteRepository;
|
||||
late GetTranslations useCase;
|
||||
|
||||
setUp(() {
|
||||
mockQuoteRepository = MockQuoteRepository();
|
||||
useCase = GetTranslations(mockQuoteRepository);
|
||||
});
|
||||
|
||||
test('should get translations', () async {
|
||||
// arrange
|
||||
final tExpected = <Translation>[];
|
||||
when(
|
||||
() => mockQuoteRepository.getTranslations(),
|
||||
).thenAnswer((_) async => right(tExpected));
|
||||
|
||||
// act
|
||||
final result = await useCase(const NoParams());
|
||||
|
||||
// assert
|
||||
expect(result, right(tExpected));
|
||||
verify(() => mockQuoteRepository.getTranslations());
|
||||
verifyNoMoreInteractions(mockQuoteRepository);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/core/domain/no_params.dart';
|
||||
import 'package:kuwot/core/error/failure.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/background_image.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_background_images.dart';
|
||||
import 'package:kuwot/features/quote/presentation/bloc/background_images_bloc.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockGetBackgroundImages extends Mock implements GetBackgroundImages {}
|
||||
|
||||
class FakeNoParams extends Fake implements NoParams {}
|
||||
|
||||
void main() {
|
||||
late MockGetBackgroundImages mockGetBackgroundImages;
|
||||
late BackgroundImagesBloc backgroundImagesBloc;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeNoParams());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockGetBackgroundImages = MockGetBackgroundImages();
|
||||
backgroundImagesBloc = BackgroundImagesBloc(
|
||||
getBackgroundImages: mockGetBackgroundImages,
|
||||
);
|
||||
});
|
||||
|
||||
test('initial state is BackgroundImagesInitial', () {
|
||||
// assert
|
||||
expect(backgroundImagesBloc.state, const BackgroundImagesInitialState());
|
||||
});
|
||||
|
||||
group('GetBackgroundImages', () {
|
||||
test(
|
||||
'should get background photos from GetBackgroundImages use case',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockGetBackgroundImages(any()),
|
||||
).thenAnswer((_) async => right(const <BackgroundImage>[]));
|
||||
|
||||
// act
|
||||
backgroundImagesBloc.add(const GetBackgroundImagesEvent());
|
||||
await untilCalled(() => mockGetBackgroundImages(any()));
|
||||
|
||||
// assert
|
||||
verify(() => mockGetBackgroundImages(any())).called(1);
|
||||
verifyNoMoreInteractions(mockGetBackgroundImages);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should emit [BackgroundImagesLoading, BackgroundImagesLoaded] when data is gotten successfully',
|
||||
() async {
|
||||
// arrange
|
||||
const tBackgroundImages = <BackgroundImage>[];
|
||||
when(
|
||||
() => mockGetBackgroundImages(any()),
|
||||
).thenAnswer((_) async => right(tBackgroundImages));
|
||||
|
||||
// assert later
|
||||
final expected = [
|
||||
const BackgroundImagesLoadingState(),
|
||||
const BackgroundImagesLoadedState(tBackgroundImages),
|
||||
];
|
||||
expectLater(backgroundImagesBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
backgroundImagesBloc.add(const GetBackgroundImagesEvent());
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should emit [BackgroundImagesLoading, BackgroundImagesError] when getting data fails',
|
||||
() async {
|
||||
// arrange
|
||||
const tFailure = UnknownFailure(message: 'Unknown Failure');
|
||||
when(
|
||||
() => mockGetBackgroundImages(any()),
|
||||
).thenAnswer((_) async => left(tFailure));
|
||||
|
||||
// assert later
|
||||
final expected = [
|
||||
const BackgroundImagesLoadingState(),
|
||||
BackgroundImagesErrorState(message: tFailure.message),
|
||||
];
|
||||
expectLater(backgroundImagesBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
backgroundImagesBloc.add(const GetBackgroundImagesEvent());
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
180
test/features/quote/presentation/bloc/quote_bloc_test.dart
Normal file
180
test/features/quote/presentation/bloc/quote_bloc_test.dart
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/core/data/local/config.dart';
|
||||
import 'package:kuwot/core/data/local/translation_target_config.dart';
|
||||
import 'package:kuwot/core/error/failure.dart';
|
||||
import 'package:kuwot/features/quote/domain/entities/quote.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_quote.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_translated_quote.dart';
|
||||
import 'package:kuwot/features/quote/presentation/bloc/quote_bloc.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockGetQuote extends Mock implements GetQuote {}
|
||||
|
||||
class MockGetTranslatedQuote extends Mock implements GetTranslatedQuote {}
|
||||
|
||||
class MockTranslationTargetConfig extends Mock
|
||||
implements Config<TranslationTarget> {}
|
||||
|
||||
class FakeQuoteParams extends Fake implements GetQuoteParams {}
|
||||
|
||||
class FakeTranslatedQuoteParams extends Fake
|
||||
implements GetTranslatedQuoteParams {}
|
||||
|
||||
void main() {
|
||||
late MockGetQuote mockGetQuote;
|
||||
late MockGetTranslatedQuote mockGetTranslatedQuote;
|
||||
late MockTranslationTargetConfig mockTranslationTargetConfig;
|
||||
late QuoteBloc quoteBloc;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeQuoteParams());
|
||||
registerFallbackValue(FakeTranslatedQuoteParams());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockGetQuote = MockGetQuote();
|
||||
mockGetTranslatedQuote = MockGetTranslatedQuote();
|
||||
mockTranslationTargetConfig = MockTranslationTargetConfig();
|
||||
|
||||
quoteBloc = QuoteBloc(
|
||||
getQuote: mockGetQuote,
|
||||
getTranslatedQuote: mockGetTranslatedQuote,
|
||||
translationTargetConfig: mockTranslationTargetConfig,
|
||||
);
|
||||
});
|
||||
|
||||
const tTranslationTarget = TranslationTarget(id: 'en', name: 'English');
|
||||
const tQuote = Quote(id: 1, author: 'author', body: 'text');
|
||||
const tFailure = UnknownFailure(message: 'Unknown Failure');
|
||||
|
||||
test('initial state is QuoteInitial', () {
|
||||
// assert
|
||||
expect(quoteBloc.state, const QuoteInitialState());
|
||||
});
|
||||
|
||||
group('GetQuote', () {
|
||||
test('should get quote from GetQuote use case', () async {
|
||||
// arrange
|
||||
when(() => mockGetQuote(any())).thenAnswer((_) async => right(tQuote));
|
||||
when(
|
||||
() => mockTranslationTargetConfig.get(),
|
||||
).thenAnswer((_) async => tTranslationTarget);
|
||||
|
||||
// act
|
||||
quoteBloc.add(const GetQuoteEvent());
|
||||
await untilCalled(() => mockTranslationTargetConfig.get());
|
||||
await untilCalled(() => mockGetQuote(any()));
|
||||
|
||||
// assert
|
||||
verify(() => mockGetQuote(any()));
|
||||
verifyNoMoreInteractions(mockGetQuote);
|
||||
});
|
||||
|
||||
test(
|
||||
'should emit [QuoteLoading, QuoteLoaded] when data is gotten successfully',
|
||||
() async {
|
||||
// arrange
|
||||
when(() => mockGetQuote(any())).thenAnswer((_) async => right(tQuote));
|
||||
when(
|
||||
() => mockTranslationTargetConfig.get(),
|
||||
).thenAnswer((_) async => tTranslationTarget);
|
||||
|
||||
// assert later
|
||||
const expected = [QuoteLoadingState(), QuoteLoadedState(quote: tQuote)];
|
||||
expectLater(quoteBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
quoteBloc.add(const GetQuoteEvent());
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should emit [QuoteLoading, QuoteError] when getting data fails',
|
||||
() async {
|
||||
// arrange
|
||||
when(() => mockGetQuote(any())).thenAnswer((_) async => left(tFailure));
|
||||
when(
|
||||
() => mockTranslationTargetConfig.get(),
|
||||
).thenAnswer((_) async => tTranslationTarget);
|
||||
|
||||
// assert later
|
||||
final expected = [
|
||||
const QuoteLoadingState(),
|
||||
QuoteErrorState(message: tFailure.message),
|
||||
];
|
||||
expectLater(quoteBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
quoteBloc.add(const GetQuoteEvent());
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('GetTranslatedQuote', () {
|
||||
test(
|
||||
'should get translated quote from GetTranslatedQuote use case',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockGetTranslatedQuote(any()),
|
||||
).thenAnswer((_) async => right(tQuote));
|
||||
when(
|
||||
() => mockTranslationTargetConfig.get(),
|
||||
).thenAnswer((_) async => tTranslationTarget);
|
||||
|
||||
// act
|
||||
quoteBloc.add(const GetTranslatedQuoteEvent(tTranslationTarget));
|
||||
await untilCalled(() => mockTranslationTargetConfig.get());
|
||||
await untilCalled(() => mockGetTranslatedQuote(any()));
|
||||
|
||||
// assert
|
||||
verify(() => mockGetTranslatedQuote(any()));
|
||||
verifyNoMoreInteractions(mockGetTranslatedQuote);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should emit [QuoteLoading, QuoteLoaded] when data is gotten successfully',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockGetTranslatedQuote(any()),
|
||||
).thenAnswer((_) async => right(tQuote));
|
||||
when(
|
||||
() => mockTranslationTargetConfig.get(),
|
||||
).thenAnswer((_) async => tTranslationTarget);
|
||||
|
||||
// assert later
|
||||
const expected = [QuoteLoadingState(), QuoteLoadedState(quote: tQuote)];
|
||||
expectLater(quoteBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
quoteBloc.add(const GetTranslatedQuoteEvent(tTranslationTarget));
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'should emit [QuoteLoading, QuoteError] when getting data fails',
|
||||
() async {
|
||||
// arrange
|
||||
when(
|
||||
() => mockGetTranslatedQuote(any()),
|
||||
).thenAnswer((_) async => left(tFailure));
|
||||
when(
|
||||
() => mockTranslationTargetConfig.get(),
|
||||
).thenAnswer((_) async => tTranslationTarget);
|
||||
|
||||
// assert later
|
||||
final expected = [
|
||||
const QuoteLoadingState(),
|
||||
QuoteErrorState(message: tFailure.message),
|
||||
];
|
||||
expectLater(quoteBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
quoteBloc.add(const GetTranslatedQuoteEvent(tTranslationTarget));
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:kuwot/core/domain/no_params.dart';
|
||||
import 'package:kuwot/core/error/failure.dart';
|
||||
import 'package:kuwot/features/quote/domain/use_cases/get_translations.dart';
|
||||
import 'package:kuwot/features/quote/presentation/bloc/translations_bloc.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MockGetTranslations extends Mock implements GetTranslations {}
|
||||
|
||||
class FakeNoParams extends Fake implements NoParams {}
|
||||
|
||||
void main() {
|
||||
late MockGetTranslations mockGetTranslations;
|
||||
late TranslationsBloc translationsBloc;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeNoParams());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockGetTranslations = MockGetTranslations();
|
||||
translationsBloc = TranslationsBloc(getTranslations: mockGetTranslations);
|
||||
});
|
||||
|
||||
group('GetTranslations', () {
|
||||
test('should get translations from GetTranslations use case', () async {
|
||||
// arrange
|
||||
when(() => mockGetTranslations(any())).thenAnswer((_) async => right([]));
|
||||
|
||||
// act
|
||||
translationsBloc.add(const GetTranslationsEvent());
|
||||
await untilCalled(() => mockGetTranslations(any()));
|
||||
|
||||
// assert
|
||||
verify(() => mockGetTranslations(any()));
|
||||
verifyNoMoreInteractions(mockGetTranslations);
|
||||
});
|
||||
|
||||
test('should emit [Loading, Loaded] when successful', () async {
|
||||
// arrange
|
||||
when(() => mockGetTranslations(any())).thenAnswer((_) async => right([]));
|
||||
|
||||
// assert later
|
||||
final expected = [
|
||||
const TranslationsLoadingState(),
|
||||
const TranslationsLoadedState(translations: []),
|
||||
];
|
||||
expectLater(translationsBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
translationsBloc.add(const GetTranslationsEvent());
|
||||
});
|
||||
|
||||
test('should emit [Loading, Error] when unsuccessful', () async {
|
||||
// arrange
|
||||
when(() => mockGetTranslations(any())).thenAnswer(
|
||||
(_) async => left(const UnknownFailure(message: 'Unknown Failure')),
|
||||
);
|
||||
|
||||
// assert later
|
||||
final expected = [
|
||||
const TranslationsLoadingState(),
|
||||
const TranslationsErrorState(message: 'Unknown Failure'),
|
||||
];
|
||||
expectLater(translationsBloc.stream, emitsInOrder(expected));
|
||||
|
||||
// act
|
||||
translationsBloc.add(const GetTranslationsEvent());
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue