جشنواره عیدانه راکت | عضویت ویژه راکت برای آخرین بار | افزایش قیمت‌ها از سال جدید | و ...

مشاهده اطلاعات بیشتر...
ثانیه
دقیقه
ساعت
روز
coarad supp
4 سال پیش توسط coarad supp مطرح شد
10 پاسخ

تست نویسی روابط many to many

@hesammousavi
@mohsenbostan
@hossein.r.1442
@ali.bayat
سلام، دوستان برای تست نویسی روابط many to many که میخوایم تست کنیم اطلاعات ارسالی درست ذخیره میشن یا خیر،باید به چه شکل تست نوشت؟
من سرچ کردم چیزی پیدا نکردم که فیچر تست نوشته شده باشه، همش با یونیت بود، ینی فیچر نمیشه؟ ممنون میشم قطعه کدی، منبعی ، راهنمایی کنید


ثبت پرسش جدید
سبحان دادخواه
تخصص : دانشجوی برنامه نویسی :)
@SobhanDadkhah 4 سال پیش مطرح شد
0

@coaradsupp سلام .
من با دانش ابتدایی خودم یک مثالی میزنم .
شما فرضا به یک روتی میخواین اطلاعات ارسال کنید و چک کنید که اطلاعات توی جدول pivot ثبت میشه . این تستی هست که اخیرا بر اساس همون دوره لاراکست که گفتم خدمتتون نوشتم . سناریو هم دعوت کردن یک کاربر به پروژه هست . که بر اساس رابطه Many to many ذخیره میشه .

 public function test_a_project_owner_can_invite_a_user()
    {

        $project = ProjectFactory::create();
        $userToInvite = factory('App\User')->create();

        $this->actingAs($project->owner)->post($project->path() . '/invitations' , [
            'email' => $userToInvite->email
        ])->assertRedirect($project->path());

        $this->assertTrue($project->members->contains($userToInvite));

    }

امیدوارم کمک کرده باشه . البته خودم تازه تست نویسی شروع کردم 😅


محسن بستان
تخصص : Senior Backend Developer
@mohsenbostan 4 سال پیش آپدیت شد
0

@coaradsupp
سلام.
راه های مختلفی وجود داره. مثال زیر یک نمونه هستش :

/** @test */
public function a_pin_can_belong_to_a_board()
{
    $board = create('App\Board');
    $pin = create('App\Pin');

    $pin->boards()->sync($board);

    $this->assertDatabaseHas('board_pin', [
        'board_id' => $board->id,
        'pin_id' => $pin->id
    ]);
}

پیشنهاد می کنم مقاله زیر هم بررسی کنید :
https://medium.com/@tonyfrenzy/part-2-testing-model-relationships-in-laravel-basic-8b606dd36c02


coarad supp
تخصص : برنامه نویس لاراول
@coaradsupp 4 سال پیش مطرح شد
0

@mohsenbostan
سلام، ممنون، اتفاقا این رو توی سرچ هام دیدم، ولی خوب یونیت تست هست، خوبه، ولی خو فیچر تست نیاز نیست نوشته بشه؟ ینی به همون کنترلر و روت درخواست ارسال بشه و پاسخ چک بشه؟

@SobhanDadkhah
سلام، ممنون
ینی واسه این مورد باید سمت تست مدل رو ایجاد کنیم و بعد از درخواست چک بشه که توی دیتابیس هست؟ من این رو دارم کار میکنم، تست نویسی برای رول ها هست که پرمیشن رو واسش سینک میکنیم

public function test_just_admin_role_can_create_role_with_correct_data()
    {
       $permission_id = Permission::where('guard_name', 'admin')->pluck('id')->toArray();
        $admin    = $this->create_user_by_role('admin');
        $role     = [
            'guard_name' => 'admin',
            'name'       => 'manager',
            'label'      => 'مدیر',
            'permission_id' => $permission_id
        ];
        $response = $this->be($admin, 'admin')
            ->from(route('admin.role.create'))
            ->post(route('admin.role.store'), $role);

        $response->assertRedirect(route('admin.role.create'));
        $this->assertDatabaseHas('roles', $role);
    }

ینی الان باید ایدی پرمیشن ها رو بفرستم، و بعد توی دیتابیس چک کنم ببینم هس یا خیر، و نمیدونم به چه شکل


سبحان دادخواه
تخصص : دانشجوی برنامه نویسی :)
@SobhanDadkhah 4 سال پیش آپدیت شد
0

@coaradsupp . دقیقا .
توی این مثال شما دارین یه ادمین انتخاب میکنید و میخواید توسط اون ادمین ریکوست رو بفرستید به روت admin.role.store . جالا توی کنترلر باید منطق خودتون رو پیاده کنید . مثلا اگر کاربر وارد شده ادمین بود اطلاعات ثبت میشه و ریدایرکت میشه و همچنین توی دیتابیستون رول جدید رو باید ببینید . دقیقا کاری که کردین درسته اما من اگر جای شما باشم این تست رو به دو قسمت تقسیم میکنم :

public function test_admins_can_create_role_with_correct_data(){}
public function test_non_admins_cannot_create_role_with_correct_data(){}

توی تست اول کاری که شما کردین رو میکنم و توی تست دوم برای اطمینان بیشتر کاربری که ادمین نیست رو میسازم و لاگین میکنم و درخواستش رو ارسال میکنم به کنترلر مربوطه . که در این صورت باید ارور 403 دریافت کنید یا هرچی که خودتون صلاح میدونید . بسته به پالیسی یا گیتی که طراحی کردین واسه احرازهویت

$this->actingAs($normalUser)->post(route('admin.role.store') , $role])->assertStatus(403);

البته با توجه به اسم تستی که دارین میتونید هردو مورد رو توی همون یدونه متد تست کنید ولی شلوغ میشه . مثلا :

public function test_only_admin_role_can_create_role_with_correct_data(){}

اسم گذاری و منطقش سلیقه ایه ولی خوب اینقدر که جفری راجب کد تمیز صحبت میکنه منم از اون اموزش پیش میرم خیلی حساس شدم 😅😅
سعی کردم چیزی که میدونم توضیح بدم خدمتتون .
اگر نیاز بود کد کاملشو رو هم مینویسم قرار میدم 🌹


coarad supp
تخصص : برنامه نویس لاراول
@coaradsupp 4 سال پیش مطرح شد
0

@SobhanDadkhah

ممنون از توضیحات خوبتون، چون من دارم از multi auth استفاده میکنم، کاربر دسترسی نداره، فقط ادمین ها هستن که رولشون رو چک میکنم،واسه اونام تست نوشتم،که دسترسی نداشته باشن، ولی برای این قسمت نمیدونم چطوری باید اون اطلاعات پرمیشن رو که میفرستم، بعدش چک کنم که توی جدول خودشون به درستی ذخیره شدن یا نه، این رو راه حلی دارین واسش؟ چون به رول ساخته شده دسترسی نداریم که توی جدول واست بررسی کنیم


سبحان دادخواه
تخصص : دانشجوی برنامه نویسی :)
@SobhanDadkhah 4 سال پیش مطرح شد
0

@coaradsupp
راستش من ساختار multi auth نمیدونم چون تاحالا استفاده نکردم ولی قاعدتا باید اینطور باشه که جدول واسط شما admin_id و role_id درسته ؟
پس با روابط الکوئنت و اون هلپر contains چک کنید که ادمین شما توی جدول واسطه رول مورد نظر بهش الحاق شده یا نه .
ممکنه شمارو هم گمراه کرده باشم چون خودم اطلاعی از ساختار دیتابیسی که باهاش کار میکنید نداشتم تاحالا😅
اگر اشتباه میکنم تصحیح کنید چون خیلی موضوع جالب و آموزنده ای میتونه واسه خودم باشه
ممنون که به اشتراک میذارید 🌹


coarad supp
تخصص : برنامه نویس لاراول
@coaradsupp 4 سال پیش مطرح شد
0

@SobhanDadkhah
واسه سطح دسترسی از این پکیج استفاده میکنم،
https://github.com/spatie/laravel-permission
همه جداول واسط دیگه مورد نیاز نیست،

کدهای تستی که نوشتم رو قرار میدم اگه دوستان حالشو داشتن و نگاه کردن ممنون میشم بگن مشکل داره یا نه

class PermissionTest extends TestCase
{
    use DatabaseTransactions;

    public function test_just_admin_role_can_view_permission_index()
    {
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')->get(route('admin.permission.index'));

        $response->assertSuccessful();
        $response->assertViewIs('panel.admin.permission.index');
    }

    public function test_another_role_cannot_viw_permission_index()
    {
        $roles = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')->get(route('admin.permission.index'));

            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_can_view_permission_create()
    {
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')->get(route('admin.permission.create'));

        $response->assertSuccessful();
        $response->assertViewIs('panel.admin.permission.create');
    }

    public function test_just_admin_role_can_create_permission_with_correct_data()
    {
        $permission = [
            'guard_name' => 'admin',
            'name'       => 'comment_index',
            'label'      => 'لیست نظرات',
        ];
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->from(route('admin.permission.create'))
            ->post(route('admin.permission.store'), $permission);

        $response->assertRedirect(route('admin.permission.create'));
        $this->assertDatabaseHas('permissions', $permission);
    }

    public function test_another_role_cannot_create_permission_with_correct_data()
    {
        $permission = [
            'guard_name' => 'admin',
            'name'       => 'comment_index',
            'label'      => 'لیست نظرات',
        ];
        $roles      = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')
                ->from(route('admin.permission.create'))
                ->post(route('admin.permission.store'), $permission);

            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_cannot_create_permission_with_incorrect_data()
    {
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')
            ->from(route('admin.permission.create'))
            ->post(route('admin.permission.store'), ['guard_name' => 'dd']);

        $response->assertRedirect(route('admin.permission.create'));
        $response->assertSessionHasErrors(['guard_name', 'name', 'label']);
    }

    public function test_just_admin_role_cannot_create_permission_with_duplicated_data()
    {
        $permission = $this->createPermission();
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->from(route('admin.permission.create'))
            ->post(route('admin.permission.store'), [
                'guard_name' => $permission->guard_name,
                'name'       => $permission->name,
                'label'      => $permission->label

            ]);

        $response->assertRedirect(route('admin.permission.create'));
        $response->assertSessionHasErrors(['name', 'label']);
    }

    public function test_just_admin_role_can_update_permission_with_correct_data()
    {
        $permission = $this->createPermission();
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->put(route('admin.permission.update', $permission), [
                'guard_name' => 'admin',
                'name'       => 'لیست نظرات1',
                'label'      => 'comment_index1',
            ]);

        $response->assertJson([
            'code' => 1
        ]);
        $this->assertDatabaseHas('permissions', [
            'guard_name' => 'admin',
            'name'       => 'لیست نظرات1',
            'label'      => 'comment_index1',
        ]);
    }

    public function test_just_admin_role_cannot_update_permission_with_incorrect_data()
    {
        $permission = $this->createPermission();
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->putJson(route('admin.permission.update', $permission), [
                'guard_name' => 'admin',
                'label'      => 'comment_index1',
            ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['name']);
    }

    public function test_another_role_cannot_update_permission_with_correct_data()
    {
        $permission = $this->createPermission();
        $roles      = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')
                ->put(route('admin.permission.update', $permission), [
                    'guard_name' => 'admin',
                    'name'       => 'لیست نظرات1',
                    'label'      => 'comment_index1',
                ]);
            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_can_delete_permission()
    {
        $permission = [
            'guard_name' => 'admin',
            'name'       => 'comment_index',
            'label'      => 'لیست نظرات',
        ];
        $permission = Permission::create($permission);
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->delete(route('admin.permission.delete', $permission));

        $response->assertJson([
            'code' => 1,
        ]);
    }

    public function test_another_role_cannot_delete_permission()
    {
        $permission = $this->createPermission();
        $roles      = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')
                ->delete(route('admin.permission.delete', $permission));

            $response->assertForbidden();
        }
    }

    private function create_user_by_role($role)
    {
        $admin = factory(Admin::class)->create();
        $admin->assignRole($role);
        return $admin;
    }

    /**
     * @return Permission|array|\Illuminate\Database\Eloquent\Model
     */
    private function createPermission()
    {
        $permission = [
            'guard_name' => 'admin',
            'name'       => 'comment_index',
            'label'      => 'لیست نظرات',
        ];
        $permission = Permission::create($permission);
        return $permission;
    }
}

class RoleTest extends TestCase
{
    use DatabaseTransactions;

    public function test_just_admin_role_can_view_role_index()
    {
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')->get(route('admin.role.index'));

        $response->assertSuccessful();
        $response->assertViewIs('panel.admin.role.index');
    }

    public function test_another_role_cannot_viw_role_index()
    {
        $roles = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')->get(route('admin.role.index'));

            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_can_view_role_create()
    {
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')->get(route('admin.role.create'));

        $response->assertSuccessful();
        $response->assertViewIs('panel.admin.role.create');
    }

    public function test_another_role_cannot_view_role_create()
    {
        $roles = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')->get(route('admin.role.create'));

            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_can_create_role_with_correct_data()
    {
        $admin      = $this->create_user_by_role('admin');
        $permission = Permission::where('guard_name', 'admin')
            ->where('name', 'admin_index')->first();
        $role       = [
            'guard_name'    => 'admin',
            'name'          => 'manager',
            'label'         => 'مدیر',
            'permission_id' => [$permission->id]
        ];
        $response   = $this->be($admin, 'admin')
            ->from(route('admin.role.create'))
            ->post(route('admin.role.store'), $role);

        $response->assertStatus(302);
        $response->assertRedirect(route('admin.role.create'));
        $this->assertDatabaseHas('roles', [
            'guard_name' => $role['guard_name'],
            'name'       => $role['name'],
            'label'      => $role['label'],
        ]);
        $role = Role::where('name', $role['name'])->first();
        $this->assertDatabaseHas('role_has_permissions', [
            'role_id'       => $role->id,
            'permission_id' => $permission->id,
        ]);
    }

    public function test_another_role_cannot_create_role_with_correct_data()
    {
        $permission = Permission::where('guard_name', 'admin')
            ->where('name', 'admin_index')->first();
        $mrole      = [
            'guard_name'    => 'admin',
            'name'          => 'manager',
            'label'         => 'مدیر',
            'permission_id' => [$permission->id]
        ];
        $roles      = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')
                ->from(route('admin.role.create'))
                ->post(route('admin.role.store'), $mrole);

            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_cannot_create_role_with_incorrect_data()
    {
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')
            ->from(route('admin.role.create'))
            ->post(route('admin.role.store'), ['guard_name' => 'dd']);

        $response->assertRedirect(route('admin.role.create'));
        $response->assertSessionHasErrors(['guard_name', 'name', 'label']);
    }

    public function test_just_admin_role_cannot_create_role_with_duplicated_data()
    {
        $role     = $this->createRole();
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')
            ->from(route('admin.role.create'))
            ->post(route('admin.role.store'), [
                'name'       => $role->name,
                'label'      => $role->label,
                'guard_name' => $role->guard_name,
            ]);

        $response->assertRedirect(route('admin.role.create'));
        $response->assertSessionHasErrors(['name', 'label']);
    }

    public function test_just_admin_role_can_update_role_with_correct_data()
    {
        $role       = $this->createRole();
        $permission = Permission::where('guard_name', 'admin')
            ->where('name', 'admin_index')->first();
        $mrole      = [
            'guard_name'    => 'admin',
            'name'          => 'comment_index1',
            'label'         => 'لیست نظرات1',
            'permission_id' => [$permission->id]
        ];
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->put(route('admin.role.update', $role), $mrole);

        $response->assertJson([
            'code' => 1
        ]);
        $this->assertDatabaseHas('roles', [
            'guard_name' => $mrole['guard_name'],
            'name'       => $mrole['name'],
            'label'      => $mrole['label'],
        ]);
        $this->assertDatabaseHas('role_has_permissions', [
            'role_id'       => $role->id,
            'permission_id' => $permission->id,
        ]);
    }

    public function test_just_admin_role_cannot_update_role_with_incorrect_data()
    {
        $role     = $this->createRole();
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')
            ->putJson(route('admin.role.update', $role), [
                'guard_name' => 'admin',
                'label'      => 'comment_index1',
            ]);

        $response->assertStatus(422);
        $response->assertJsonValidationErrors(['name']);
    }

    public function test_another_role_cannot_update_role_with_correct_data()
    {
        $mrole      = $this->createRole();
        $permission = Permission::where('guard_name', 'admin')
            ->where('name', 'admin_index')->first();
        $roles      = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')
                ->put(route('admin.role.update', $mrole), [
                    'guard_name'    => 'admin',
                    'name'          => 'comment_index1',
                    'label'         => 'لیست نظرات1',
                    'permission_id' => [$permission->id]
                ]);
            $response->assertForbidden();
        }
    }

    public function test_just_admin_role_can_delete_role()
    {
        $role     = $this->createRole();
        $admin    = $this->create_user_by_role('admin');
        $response = $this->be($admin, 'admin')
            ->delete(route('admin.role.delete', $role));

        $response->assertJson([
            'code' => 1,
        ]);
    }

    public function test_another_role_cannot_delete_role()
    {
        $role  = $this->createRole();
        $roles = Role::where('name', '!=', 'admin')->get();
        foreach ($roles as $role) {
            $admin    = $this->create_user_by_role($role->name);
            $response = $this->be($admin, 'admin')
                ->delete(route('admin.role.delete', $role));

            $response->assertForbidden();
        }
    }

    private function create_user_by_role($role)
    {
        $admin = factory(Admin::class)->create();
        $admin->assignRole($role);
        return $admin;
    }

    private function createRole()
    {
        $role = [
            'guard_name' => 'admin',
            'name'       => 'manager',
            'label'      => 'مدیر',
        ];
        $role = Role::create($role);
        return $role;
    }
}

سبحان دادخواه
تخصص : دانشجوی برنامه نویسی :)
@SobhanDadkhah 4 سال پیش مطرح شد
0

@coaradsupp سلام مجدد.
منطق تست هاتون تا جایی که من میدونم کاملا درسته .
واسه خودم شخصا خیلی خیلی آموزنده بود . 😎
به عنوان فرد مبتدی توی تست نویسی از trait RefreshDatabase استفاده میکنم که روی دیتابیس sqlite و مموری ست میکنم و باعث شدین DatabaseTransactions رو یاد بگیرم . 😅
یه مورد ریز بخوام بگم واسه تمیز شدن جاهایی مثل این مورد :

  public function test_just_admin_role_can_update_permission_with_correct_data()
    {
        $permission = $this->createPermission();
        $admin      = $this->create_user_by_role('admin');
        $response   = $this->be($admin, 'admin')
            ->put(route('admin.permission.update', $permission), $attributes = [
                'guard_name' => 'admin',
                'name' => 'لیست نظرات1',
                'label'=> 'comment_index1',
            ]);

        $response->assertJson([
            'code' => 1
        ]);
        $this->assertDatabaseHas('permissions', $attributes);
    }

همون موقع پاس دادن اولیه توی متغیر هم میتونید ذخیره کنید که از تکرار جلوگیری بشه . واقعا از خوبی های php هست .
البته که همیشه حالت بهتری هم وجود داره و خوب اگر دوستان با تجربه تر @ali.bayat بتونن در صورتی که وقت داشته باشن ایراداتشو بگن و روشهای best practice واسه اینجور تست نویسی رو بیان کنند قطعا خیلی میتونه آموزنده باشه 🌹


علی بیات
تخصص : توسعه دهنده ارشد وب
@ali.bayat 4 سال پیش مطرح شد
1

@coaradsupp
من یک سری توضیحات عمومی رو براتون قرار میدم

به نظر من مهمترین چیز برای نوشتن تست.. اینه که سبک توسعه آزمون محور رو به درستی درک کنیم
در اکثر موارد ما باید ۳ مرحله اصلی هر تست رو در نظر داشته باشیم:

  • وقتی این داده ها رو دارم
  • اگر این کار رو باهاش انجام بدم
  • انتظار چنین نتیجه ای رو دارم

فرق چندانی هم نداره که این یونیت تست باشه یا فیچر تست..

فیچر تست ها معمولا طولانی ترند و حالات مختلفی رو باید Assert کرد، تا ویژگی رو که میخواهید بتونید آزمایش کنید...
و یونیت تست ها میشه گفت: تست های سخت گیرانه ای هستند که با ریز شدن در جزیئات عملکرد سیستم و به صورت low-level چک میکنید

حتما Assertion های مختلف رو در مستندات لاراول و یا PHPUnit بررسی کنید و با حالات مختلف آشنا بشید (اگر قبلا این کار رو نکردید)

@SobhanDadkhah

درود
در موارد خاص میتونیم از متد setUp استفاده کنیم و مثلا اگر تست ما کلا با ادمین در تعامل هست ... مواردی مثل ساخت ادمین و لاگین کردنش رو در اون انجام بدیم..
و یا میشه با روش شئ گرایانه تری با قضیه برخورد کرد، و داده هایی رو که داریم رو در کلاس ها و متدهای خاص جا داد.

به نظرم مهمترین ویژگی TDD این هست که ما میتونیم راجع به API ی که قراره داشته باشیم (و هنوز نداریم) فکر کنیم.


coarad supp
تخصص : برنامه نویس لاراول
@coaradsupp 4 سال پیش مطرح شد
1

@SobhanDadkhah
سلام، منون به خاطر پاسختون، بله خو همین جاهاس که میتونیم کدهامونو ب اشتراک بذاریم و بررسی کنیم ببینیم اصولی هست یا خیر و راه اصولیش چی هست، و ممنون به خاطر اون نکته ای که فرمودین

@ali.bayat
سلام و بسیار ممنون از پاسختون که همیشه بسیار خوب و اموزنده هست
نمیدونم کدها رو بررسی کردین یا خیر، میخواستم ببینم اصولی هست؟ یا خیر؟ کجاهاشو باید بیشتر کار کنم ممنون باز هم


برای ارسال پاسخ لازم است وارد شده یا ثبت‌نام کنید

ورود یا ثبت‌نام